]>
Commit | Line | Data |
---|---|---|
416c1de9 ZL |
1 | /* |
2 | * inline_data.c --- data in inode | |
3 | * | |
4 | * Copyright (C) 2012 Zheng Liu <wenqing.lz@taobao.com> | |
5 | * | |
6 | * %Begin-Header% | |
7 | * This file may be redistributed under the terms of the GNU library | |
8 | * General Public License, version 2. | |
9 | * %End-Header% | |
10 | */ | |
11 | ||
12 | #include "config.h" | |
13 | #include <stdio.h> | |
14 | #include <time.h> | |
bbccc6f3 | 15 | #include <limits.h> /* for PATH_MAX */ |
416c1de9 ZL |
16 | |
17 | #include "ext2_fs.h" | |
18 | #include "ext2_ext_attr.h" | |
19 | ||
20 | #include "ext2fs.h" | |
21 | #include "ext2fsP.h" | |
22 | ||
23 | struct ext2_inline_data { | |
24 | ext2_filsys fs; | |
25 | ext2_ino_t ino; | |
26 | size_t ea_size; /* the size of inline data in ea area */ | |
27 | void *ea_data; | |
28 | }; | |
29 | ||
30 | static errcode_t ext2fs_inline_data_ea_set(struct ext2_inline_data *data) | |
31 | { | |
32 | struct ext2_xattr_handle *handle; | |
33 | errcode_t retval; | |
34 | ||
35 | retval = ext2fs_xattrs_open(data->fs, data->ino, &handle); | |
36 | if (retval) | |
37 | return retval; | |
38 | ||
39 | retval = ext2fs_xattrs_read(handle); | |
40 | if (retval) | |
41 | goto err; | |
42 | ||
43 | retval = ext2fs_xattr_set(handle, "system.data", | |
44 | data->ea_data, data->ea_size); | |
416c1de9 ZL |
45 | err: |
46 | (void) ext2fs_xattrs_close(&handle); | |
47 | return retval; | |
48 | } | |
49 | ||
50 | static errcode_t ext2fs_inline_data_ea_get(struct ext2_inline_data *data) | |
51 | { | |
52 | struct ext2_xattr_handle *handle; | |
53 | errcode_t retval; | |
54 | ||
55 | data->ea_size = 0; | |
56 | data->ea_data = 0; | |
57 | ||
58 | retval = ext2fs_xattrs_open(data->fs, data->ino, &handle); | |
59 | if (retval) | |
60 | return retval; | |
61 | ||
62 | retval = ext2fs_xattrs_read(handle); | |
63 | if (retval) | |
64 | goto err; | |
65 | ||
66 | retval = ext2fs_xattr_get(handle, "system.data", | |
67 | (void **)&data->ea_data, &data->ea_size); | |
996999a1 DW |
68 | if (retval == EXT2_ET_EA_KEY_NOT_FOUND) { |
69 | data->ea_size = 0; | |
70 | data->ea_data = NULL; | |
71 | retval = 0; | |
72 | } else if (retval) | |
416c1de9 ZL |
73 | goto err; |
74 | ||
75 | err: | |
76 | (void) ext2fs_xattrs_close(&handle); | |
77 | return retval; | |
78 | } | |
79 | ||
82e77d07 ZL |
80 | errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino) |
81 | { | |
82 | struct ext2_inline_data data; | |
8f8511ab | 83 | char empty[1] = { '\0' }; |
82e77d07 ZL |
84 | |
85 | data.fs = fs; | |
86 | data.ino = ino; | |
87 | data.ea_size = 0; | |
8f8511ab | 88 | data.ea_data = empty; |
82e77d07 ZL |
89 | return ext2fs_inline_data_ea_set(&data); |
90 | } | |
91 | ||
133e9462 ZL |
92 | errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino, size_t *size) |
93 | { | |
94 | struct ext2_inode inode; | |
95 | struct ext2_inline_data data; | |
96 | errcode_t retval; | |
97 | ||
98 | retval = ext2fs_read_inode(fs, ino, &inode); | |
99 | if (retval) | |
100 | return retval; | |
101 | ||
102 | if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) | |
103 | return EXT2_ET_NO_INLINE_DATA; | |
104 | ||
105 | data.fs = fs; | |
106 | data.ino = ino; | |
107 | retval = ext2fs_inline_data_ea_get(&data); | |
108 | if (retval) | |
109 | return retval; | |
110 | ||
111 | *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size; | |
112 | return ext2fs_free_mem(&data.ea_data); | |
113 | } | |
416c1de9 ZL |
114 | |
115 | int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, | |
116 | void *priv_data) | |
117 | { | |
118 | struct dir_context *ctx; | |
119 | struct ext2_inode inode; | |
120 | struct ext2_dir_entry dirent; | |
121 | struct ext2_inline_data data; | |
122 | int ret = BLOCK_ABORT; | |
123 | e2_blkcnt_t blockcnt = 0; | |
8f22fa05 DW |
124 | char *old_buf; |
125 | unsigned int old_buflen; | |
126 | int old_flags; | |
416c1de9 ZL |
127 | |
128 | ctx = (struct dir_context *)priv_data; | |
8f22fa05 DW |
129 | old_buf = ctx->buf; |
130 | old_buflen = ctx->buflen; | |
131 | old_flags = ctx->flags; | |
132 | ctx->flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA; | |
416c1de9 ZL |
133 | |
134 | ctx->errcode = ext2fs_read_inode(fs, ino, &inode); | |
135 | if (ctx->errcode) | |
136 | goto out; | |
137 | ||
138 | if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) { | |
139 | ctx->errcode = EXT2_ET_NO_INLINE_DATA; | |
140 | goto out; | |
141 | } | |
142 | ||
143 | if (!LINUX_S_ISDIR(inode.i_mode)) { | |
144 | ctx->errcode = EXT2_ET_NO_DIRECTORY; | |
145 | goto out; | |
146 | } | |
147 | ret = 0; | |
148 | ||
149 | /* we first check '.' and '..' dir */ | |
150 | dirent.inode = ino; | |
151 | dirent.name_len = 1; | |
152 | ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent); | |
153 | dirent.name[0] = '.'; | |
154 | dirent.name[1] = '\0'; | |
155 | ctx->buf = (char *)&dirent; | |
156 | ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); | |
157 | ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); | |
158 | if (ret & BLOCK_ABORT) | |
159 | goto out; | |
160 | ||
161 | dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]); | |
162 | dirent.name_len = 2; | |
163 | ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent); | |
164 | dirent.name[0] = '.'; | |
165 | dirent.name[1] = '.'; | |
166 | dirent.name[2] = '\0'; | |
167 | ctx->buf = (char *)&dirent; | |
168 | ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); | |
169 | ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); | |
170 | if (ret & BLOCK_INLINE_DATA_CHANGED) { | |
171 | errcode_t err; | |
172 | ||
173 | inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode); | |
174 | err = ext2fs_write_inode(fs, ino, &inode); | |
175 | if (err) | |
176 | goto out; | |
177 | ret &= ~BLOCK_INLINE_DATA_CHANGED; | |
178 | } | |
179 | if (ret & BLOCK_ABORT) | |
180 | goto out; | |
181 | ||
182 | ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE; | |
183 | ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE; | |
184 | #ifdef WORDS_BIGENDIAN | |
185 | ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); | |
186 | if (ctx->errcode) { | |
187 | ret |= BLOCK_ABORT; | |
188 | goto out; | |
189 | } | |
190 | #endif | |
191 | ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); | |
192 | if (ret & BLOCK_INLINE_DATA_CHANGED) { | |
193 | #ifdef WORDS_BIGENDIAN | |
194 | ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, | |
195 | ctx->buflen, 0); | |
196 | if (ctx->errcode) { | |
197 | ret |= BLOCK_ABORT; | |
198 | goto out; | |
199 | } | |
200 | #endif | |
201 | ctx->errcode = ext2fs_write_inode(fs, ino, &inode); | |
202 | if (ctx->errcode) | |
203 | ret |= BLOCK_ABORT; | |
204 | ret &= ~BLOCK_INLINE_DATA_CHANGED; | |
205 | } | |
206 | if (ret & BLOCK_ABORT) | |
207 | goto out; | |
208 | ||
209 | data.fs = fs; | |
210 | data.ino = ino; | |
211 | ctx->errcode = ext2fs_inline_data_ea_get(&data); | |
212 | if (ctx->errcode) { | |
213 | ret |= BLOCK_ABORT; | |
214 | goto out; | |
215 | } | |
216 | if (data.ea_size <= 0) | |
657f508c | 217 | goto out1; |
416c1de9 ZL |
218 | |
219 | ctx->buf = data.ea_data; | |
220 | ctx->buflen = data.ea_size; | |
221 | #ifdef WORDS_BIGENDIAN | |
251edc3d EG |
222 | ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); |
223 | if (ctx->errcode) { | |
416c1de9 | 224 | ret |= BLOCK_ABORT; |
657f508c | 225 | goto out1; |
416c1de9 ZL |
226 | } |
227 | #endif | |
228 | ||
229 | ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); | |
230 | if (ret & BLOCK_INLINE_DATA_CHANGED) { | |
231 | #ifdef WORDS_BIGENDIAN | |
232 | ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, | |
233 | ctx->buflen, 0); | |
234 | if (ctx->errcode) { | |
235 | ret |= BLOCK_ABORT; | |
236 | goto out1; | |
237 | } | |
238 | #endif | |
239 | ctx->errcode = ext2fs_inline_data_ea_set(&data); | |
240 | if (ctx->errcode) | |
241 | ret |= BLOCK_ABORT; | |
242 | } | |
243 | ||
244 | out1: | |
245 | ext2fs_free_mem(&data.ea_data); | |
416c1de9 | 246 | out: |
8f22fa05 DW |
247 | ctx->buf = old_buf; |
248 | ctx->buflen = old_buflen; | |
249 | ctx->flags = old_flags; | |
416c1de9 ZL |
250 | ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED); |
251 | return ret; | |
252 | } | |
46bd6bdf | 253 | |
97581d44 | 254 | errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino) |
46bd6bdf ZL |
255 | { |
256 | struct ext2_xattr_handle *handle; | |
257 | errcode_t retval; | |
258 | ||
259 | retval = ext2fs_xattrs_open(fs, ino, &handle); | |
260 | if (retval) | |
261 | return retval; | |
262 | ||
263 | retval = ext2fs_xattrs_read(handle); | |
264 | if (retval) | |
265 | goto err; | |
266 | ||
267 | retval = ext2fs_xattr_remove(handle, "system.data"); | |
46bd6bdf ZL |
268 | err: |
269 | (void) ext2fs_xattrs_close(&handle); | |
270 | return retval; | |
271 | } | |
272 | ||
273 | static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino, | |
274 | char *bbuf, char *ibuf, int size) | |
275 | { | |
276 | struct ext2_dir_entry *dir, *dir2; | |
277 | struct ext2_dir_entry_tail *t; | |
278 | errcode_t retval; | |
8f8511ab TT |
279 | int offset; |
280 | unsigned int rec_len; | |
46bd6bdf ZL |
281 | int csum_size = 0; |
282 | int filetype = 0; | |
46bd6bdf | 283 | |
77b3e987 | 284 | if (ext2fs_has_feature_metadata_csum(fs->super)) |
46bd6bdf ZL |
285 | csum_size = sizeof(struct ext2_dir_entry_tail); |
286 | ||
287 | /* Create '.' and '..' */ | |
77b3e987 | 288 | if (ext2fs_has_feature_filetype(fs->super)) |
46bd6bdf ZL |
289 | filetype = EXT2_FT_DIR; |
290 | ||
291 | /* | |
292 | * Set up entry for '.' | |
293 | */ | |
294 | dir = (struct ext2_dir_entry *) bbuf; | |
295 | dir->inode = ino; | |
296 | ext2fs_dirent_set_name_len(dir, 1); | |
297 | ext2fs_dirent_set_file_type(dir, filetype); | |
298 | dir->name[0] = '.'; | |
299 | rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); | |
300 | dir->rec_len = EXT2_DIR_REC_LEN(1); | |
301 | ||
302 | /* | |
303 | * Set up entry for '..' | |
304 | */ | |
305 | dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len); | |
306 | dir->rec_len = EXT2_DIR_REC_LEN(2); | |
307 | dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]); | |
308 | ext2fs_dirent_set_name_len(dir, 2); | |
309 | ext2fs_dirent_set_file_type(dir, filetype); | |
310 | dir->name[0] = '.'; | |
311 | dir->name[1] = '.'; | |
312 | ||
313 | /* | |
055866d8 | 314 | * Adjust the last rec_len |
46bd6bdf ZL |
315 | */ |
316 | offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2); | |
317 | dir = (struct ext2_dir_entry *) (bbuf + offset); | |
318 | memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE, | |
319 | size - EXT4_INLINE_DATA_DOTDOT_SIZE); | |
320 | size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) - | |
321 | EXT4_INLINE_DATA_DOTDOT_SIZE; | |
322 | ||
323 | do { | |
324 | dir2 = dir; | |
325 | retval = ext2fs_get_rec_len(fs, dir, &rec_len); | |
326 | if (retval) | |
327 | goto err; | |
328 | offset += rec_len; | |
329 | dir = (struct ext2_dir_entry *) (bbuf + offset); | |
330 | } while (offset < size); | |
331 | rec_len += fs->blocksize - csum_size - offset; | |
332 | retval = ext2fs_set_rec_len(fs, rec_len, dir2); | |
333 | if (retval) | |
334 | goto err; | |
335 | ||
336 | if (csum_size) { | |
337 | t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize); | |
338 | ext2fs_initialize_dirent_tail(fs, t); | |
339 | } | |
340 | ||
341 | err: | |
342 | return retval; | |
343 | } | |
344 | ||
54e880b8 ZL |
345 | static errcode_t |
346 | ext2fs_inline_data_dir_expand(ext2_filsys fs, ext2_ino_t ino, | |
347 | struct ext2_inode *inode, char *buf, size_t size) | |
348 | { | |
349 | errcode_t retval; | |
350 | blk64_t blk; | |
351 | char *blk_buf; | |
352 | ||
353 | retval = ext2fs_get_memzero(fs->blocksize, &blk_buf); | |
354 | if (retval) | |
355 | return retval; | |
356 | ||
357 | #ifdef WORDS_BIGENDIAN | |
cd971869 DW |
358 | retval = ext2fs_dirent_swab_in2(fs, buf + EXT4_INLINE_DATA_DOTDOT_SIZE, |
359 | size, 0); | |
54e880b8 ZL |
360 | if (retval) |
361 | goto errout; | |
362 | #endif | |
363 | ||
364 | /* Adjust the rec_len */ | |
365 | retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, buf, size); | |
cd971869 DW |
366 | if (retval) |
367 | goto errout; | |
54e880b8 ZL |
368 | /* Allocate a new block */ |
369 | retval = ext2fs_new_block2(fs, 0, 0, &blk); | |
370 | if (retval) | |
371 | goto errout; | |
372 | retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino); | |
373 | if (retval) | |
374 | goto errout; | |
375 | ||
376 | /* Update inode */ | |
77b3e987 | 377 | if (ext2fs_has_feature_extents(fs->super)) |
54e880b8 ZL |
378 | inode->i_flags |= EXT4_EXTENTS_FL; |
379 | inode->i_flags &= ~EXT4_INLINE_DATA_FL; | |
97bd89cd DW |
380 | retval = ext2fs_iblk_add_blocks(fs, inode, 1); |
381 | if (retval) | |
382 | goto errout; | |
54e880b8 ZL |
383 | inode->i_size = fs->blocksize; |
384 | retval = ext2fs_bmap2(fs, ino, inode, 0, BMAP_SET, 0, 0, &blk); | |
385 | if (retval) | |
386 | goto errout; | |
387 | retval = ext2fs_write_inode(fs, ino, inode); | |
388 | if (retval) | |
389 | goto errout; | |
390 | ext2fs_block_alloc_stats(fs, blk, +1); | |
391 | ||
392 | errout: | |
393 | ext2fs_free_mem(&blk_buf); | |
394 | return retval; | |
395 | } | |
396 | ||
397 | static errcode_t | |
398 | ext2fs_inline_data_file_expand(ext2_filsys fs, ext2_ino_t ino, | |
399 | struct ext2_inode *inode, char *buf, size_t size) | |
400 | { | |
401 | ext2_file_t e2_file; | |
402 | errcode_t retval; | |
403 | ||
404 | /* Update inode */ | |
3548bb64 | 405 | memset(inode->i_block, 0, sizeof(inode->i_block)); |
77b3e987 | 406 | if (ext2fs_has_feature_extents(fs->super)) { |
3548bb64 DW |
407 | ext2_extent_handle_t handle; |
408 | ||
409 | inode->i_flags &= ~EXT4_EXTENTS_FL; | |
410 | retval = ext2fs_extent_open2(fs, ino, inode, &handle); | |
411 | if (retval) | |
412 | return retval; | |
413 | ext2fs_extent_free(handle); | |
54e880b8 ZL |
414 | } |
415 | inode->i_flags &= ~EXT4_INLINE_DATA_FL; | |
54e880b8 ZL |
416 | inode->i_size = 0; |
417 | retval = ext2fs_write_inode(fs, ino, inode); | |
418 | if (retval) | |
419 | return retval; | |
420 | ||
421 | /* Write out the block buffer */ | |
422 | retval = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file); | |
423 | if (retval) | |
424 | return retval; | |
425 | retval = ext2fs_file_write(e2_file, buf, size, 0); | |
426 | ext2fs_file_close(e2_file); | |
427 | return retval; | |
428 | } | |
429 | ||
46bd6bdf ZL |
430 | errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino) |
431 | { | |
432 | struct ext2_inode inode; | |
433 | struct ext2_inline_data data; | |
434 | errcode_t retval; | |
54e880b8 | 435 | size_t inline_size; |
46bd6bdf | 436 | char *inline_buf = 0; |
46bd6bdf ZL |
437 | |
438 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); | |
439 | ||
440 | retval = ext2fs_read_inode(fs, ino, &inode); | |
441 | if (retval) | |
442 | return retval; | |
443 | ||
444 | if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) | |
445 | return EXT2_ET_NO_INLINE_DATA; | |
446 | ||
46bd6bdf ZL |
447 | data.fs = fs; |
448 | data.ino = ino; | |
449 | retval = ext2fs_inline_data_ea_get(&data); | |
450 | if (retval) | |
451 | return retval; | |
54e880b8 ZL |
452 | inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE; |
453 | retval = ext2fs_get_mem(inline_size, &inline_buf); | |
46bd6bdf ZL |
454 | if (retval) |
455 | goto errout; | |
456 | ||
457 | memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE); | |
458 | if (data.ea_size > 0) { | |
459 | memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE, | |
460 | data.ea_data, data.ea_size); | |
461 | } | |
462 | ||
46bd6bdf | 463 | memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE); |
179e3bd7 DW |
464 | /* |
465 | * NOTE: We must do this write -> ea_remove -> read cycle here because | |
466 | * removing the inline data EA can free the EA block, which is a change | |
467 | * that our stack copy of the inode will never see. If that happens, | |
468 | * we can end up with the EA block and lblk 0 pointing to the same | |
469 | * pblk, which is bad news. | |
470 | */ | |
471 | retval = ext2fs_write_inode(fs, ino, &inode); | |
472 | if (retval) | |
473 | goto errout; | |
46bd6bdf | 474 | retval = ext2fs_inline_data_ea_remove(fs, ino); |
179e3bd7 DW |
475 | if (retval) |
476 | goto errout; | |
477 | retval = ext2fs_read_inode(fs, ino, &inode); | |
46bd6bdf ZL |
478 | if (retval) |
479 | goto errout; | |
480 | ||
54e880b8 ZL |
481 | if (LINUX_S_ISDIR(inode.i_mode)) { |
482 | retval = ext2fs_inline_data_dir_expand(fs, ino, &inode, | |
483 | inline_buf, inline_size); | |
484 | } else { | |
485 | retval = ext2fs_inline_data_file_expand(fs, ino, &inode, | |
486 | inline_buf, inline_size); | |
487 | } | |
46bd6bdf ZL |
488 | |
489 | errout: | |
46bd6bdf ZL |
490 | if (inline_buf) |
491 | ext2fs_free_mem(&inline_buf); | |
492 | ext2fs_free_mem(&data.ea_data); | |
493 | return retval; | |
494 | } | |
54e880b8 ZL |
495 | |
496 | /* | |
497 | * When caller uses this function to retrieve the inline data, it must | |
498 | * allocate a buffer which has the size of inline data. The size of | |
499 | * inline data can be know by ext2fs_inline_data_get_size(). | |
500 | */ | |
501 | errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino, | |
502 | struct ext2_inode *inode, | |
503 | void *buf, size_t *size) | |
504 | { | |
505 | struct ext2_inode inode_buf; | |
506 | struct ext2_inline_data data; | |
507 | errcode_t retval; | |
508 | ||
509 | if (!inode) { | |
510 | retval = ext2fs_read_inode(fs, ino, &inode_buf); | |
511 | if (retval) | |
512 | return retval; | |
513 | inode = &inode_buf; | |
514 | } | |
515 | ||
516 | data.fs = fs; | |
517 | data.ino = ino; | |
518 | retval = ext2fs_inline_data_ea_get(&data); | |
519 | if (retval) | |
520 | return retval; | |
521 | ||
522 | memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE); | |
523 | if (data.ea_size > 0) | |
8f8511ab | 524 | memcpy((char *) buf + EXT4_MIN_INLINE_DATA_SIZE, |
54e880b8 ZL |
525 | data.ea_data, data.ea_size); |
526 | ||
527 | if (size) | |
528 | *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size; | |
529 | ext2fs_free_mem(&data.ea_data); | |
530 | return 0; | |
531 | } | |
532 | ||
533 | errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino, | |
534 | struct ext2_inode *inode, | |
535 | void *buf, size_t size) | |
536 | { | |
537 | struct ext2_inode inode_buf; | |
198a2d0a EB |
538 | struct ext2_inline_data data = { |
539 | .fs = fs, | |
540 | .ino = ino, | |
541 | }; | |
54e880b8 | 542 | errcode_t retval; |
cb803fc2 | 543 | size_t free_ea_size, existing_size, free_inode_size; |
54e880b8 ZL |
544 | |
545 | if (!inode) { | |
546 | retval = ext2fs_read_inode(fs, ino, &inode_buf); | |
547 | if (retval) | |
548 | return retval; | |
549 | inode = &inode_buf; | |
550 | } | |
551 | ||
552 | if (size <= EXT4_MIN_INLINE_DATA_SIZE) { | |
553 | memcpy((void *)inode->i_block, buf, size); | |
198a2d0a EB |
554 | } else { |
555 | retval = ext2fs_xattr_inode_max_size(fs, ino, &free_ea_size); | |
556 | if (retval) | |
557 | return retval; | |
54e880b8 | 558 | |
198a2d0a EB |
559 | retval = ext2fs_inline_data_size(fs, ino, &existing_size); |
560 | if (retval) | |
561 | return retval; | |
cb803fc2 | 562 | |
198a2d0a EB |
563 | if (existing_size < EXT4_MIN_INLINE_DATA_SIZE) { |
564 | free_inode_size = EXT4_MIN_INLINE_DATA_SIZE - | |
565 | existing_size; | |
566 | } else { | |
567 | free_inode_size = 0; | |
568 | } | |
cb803fc2 | 569 | |
198a2d0a EB |
570 | if (size != existing_size && |
571 | size > existing_size + free_ea_size + free_inode_size) | |
572 | return EXT2_ET_INLINE_DATA_NO_SPACE; | |
54e880b8 | 573 | |
198a2d0a EB |
574 | memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE); |
575 | if (size > EXT4_MIN_INLINE_DATA_SIZE) | |
576 | data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE; | |
577 | data.ea_data = (char *) buf + EXT4_MIN_INLINE_DATA_SIZE; | |
578 | } | |
54e880b8 ZL |
579 | retval = ext2fs_write_inode(fs, ino, inode); |
580 | if (retval) | |
581 | return retval; | |
54e880b8 ZL |
582 | return ext2fs_inline_data_ea_set(&data); |
583 | } | |
31253488 ZL |
584 | |
585 | #ifdef DEBUG | |
586 | #include "e2p/e2p.h" | |
587 | ||
588 | /* | |
589 | * The length of buffer is set to 64 because in inode's i_block member it only | |
590 | * can save 60 bytes. Thus this value can let the data being saved in extra | |
591 | * space. | |
592 | */ | |
593 | #define BUFF_SIZE (64) | |
594 | ||
595 | static errcode_t file_test(ext2_filsys fs) | |
596 | { | |
597 | struct ext2_inode inode; | |
598 | ext2_ino_t newfile; | |
599 | errcode_t retval; | |
600 | size_t size; | |
601 | char *buf = 0, *cmpbuf = 0; | |
31253488 ZL |
602 | |
603 | /* create a new file */ | |
604 | retval = ext2fs_new_inode(fs, 2, 010755, 0, &newfile); | |
605 | if (retval) { | |
ce20096f | 606 | com_err("file_test", retval, "while allocating a new inode"); |
31253488 ZL |
607 | return 1; |
608 | } | |
609 | ||
610 | memset(&inode, 0, sizeof(inode)); | |
611 | inode.i_flags |= EXT4_INLINE_DATA_FL; | |
612 | inode.i_size = EXT4_MIN_INLINE_DATA_SIZE; | |
613 | inode.i_mode = LINUX_S_IFREG; | |
614 | retval = ext2fs_write_new_inode(fs, newfile, &inode); | |
615 | if (retval) { | |
ce20096f | 616 | com_err("file_test", retval, "while writing a new inode"); |
31253488 ZL |
617 | return 1; |
618 | } | |
619 | ||
620 | retval = ext2fs_inline_data_init(fs, newfile); | |
621 | if (retval) { | |
622 | com_err("file_test", retval, "while init 'system.data'"); | |
623 | return 1; | |
624 | } | |
625 | ||
626 | retval = ext2fs_inline_data_size(fs, newfile, &size); | |
627 | if (retval) { | |
628 | com_err("file_test", retval, "while getting size"); | |
629 | return 1; | |
630 | } | |
631 | ||
632 | if (size != EXT4_MIN_INLINE_DATA_SIZE) { | |
633 | fprintf(stderr, | |
634 | "tst_inline_data: size of inline data is wrong\n"); | |
635 | return 1; | |
636 | } | |
637 | ||
638 | ext2fs_get_mem(BUFF_SIZE, &buf); | |
639 | memset(buf, 'a', BUFF_SIZE); | |
640 | retval = ext2fs_inline_data_set(fs, newfile, 0, buf, BUFF_SIZE); | |
641 | if (retval) { | |
642 | com_err("file_test", retval, | |
643 | "while setting inline data %s", buf); | |
644 | goto err; | |
645 | } | |
646 | ||
647 | ext2fs_get_mem(BUFF_SIZE, &cmpbuf); | |
648 | retval = ext2fs_inline_data_get(fs, newfile, 0, cmpbuf, &size); | |
649 | if (retval) { | |
650 | com_err("file_test", retval, "while getting inline data"); | |
651 | goto err; | |
652 | } | |
653 | ||
654 | if (size != BUFF_SIZE) { | |
655 | fprintf(stderr, | |
37176e14 | 656 | "tst_inline_data: size %zu != buflen %u\n", |
31253488 ZL |
657 | size, BUFF_SIZE); |
658 | retval = 1; | |
659 | goto err; | |
660 | } | |
661 | ||
662 | if (memcmp(buf, cmpbuf, BUFF_SIZE)) { | |
663 | fprintf(stderr, "tst_inline_data: buf != cmpbuf\n"); | |
664 | retval = 1; | |
665 | goto err; | |
666 | } | |
667 | ||
668 | retval = ext2fs_punch(fs, newfile, 0, 0, 0, ~0ULL); | |
669 | if (retval) { | |
670 | com_err("file_test", retval, "while truncating inode"); | |
671 | goto err; | |
672 | } | |
673 | ||
674 | /* reload inode and check isize */ | |
675 | ext2fs_read_inode(fs, newfile, &inode); | |
676 | if (inode.i_size != 0) { | |
677 | fprintf(stderr, "tst_inline_data: i_size should be 0\n"); | |
678 | retval = 1; | |
679 | } | |
680 | ||
681 | err: | |
682 | if (cmpbuf) | |
683 | ext2fs_free_mem(&cmpbuf); | |
684 | if (buf) | |
685 | ext2fs_free_mem(&buf); | |
686 | return retval; | |
687 | } | |
688 | ||
689 | static errcode_t dir_test(ext2_filsys fs) | |
690 | { | |
691 | const char *dot_name = "."; | |
692 | const char *stub_name = "stub"; | |
693 | const char *parent_name = "test"; | |
694 | ext2_ino_t parent, dir, tmp; | |
695 | errcode_t retval; | |
4d32184d | 696 | char dirname[32]; |
31253488 ZL |
697 | int i; |
698 | ||
699 | retval = ext2fs_mkdir(fs, 11, 11, stub_name); | |
700 | if (retval) { | |
701 | com_err("dir_test", retval, "while creating %s dir", stub_name); | |
702 | return retval; | |
703 | } | |
704 | ||
705 | retval = ext2fs_mkdir(fs, 11, 0, parent_name); | |
706 | if (retval) { | |
707 | com_err("dir_test", retval, | |
708 | "while creating %s dir", parent_name); | |
709 | return retval; | |
710 | } | |
711 | ||
712 | retval = ext2fs_lookup(fs, 11, parent_name, strlen(parent_name), | |
713 | 0, &parent); | |
714 | if (retval) { | |
715 | com_err("dir_test", retval, | |
716 | "while looking up %s dir", parent_name); | |
717 | return retval; | |
718 | } | |
719 | ||
720 | retval = ext2fs_lookup(fs, parent, dot_name, strlen(dot_name), | |
721 | 0, &tmp); | |
722 | if (retval) { | |
723 | com_err("dir_test", retval, | |
724 | "while looking up %s dir", parent_name); | |
725 | return retval; | |
726 | } | |
727 | ||
728 | if (parent != tmp) { | |
bbccc6f3 | 729 | fprintf(stderr, "tst_inline_data: parent (%u) != tmp (%u)\n", |
31253488 ZL |
730 | parent, tmp); |
731 | return 1; | |
732 | } | |
733 | ||
734 | for (i = 0, dir = 13; i < 4; i++, dir++) { | |
735 | tmp = 0; | |
bbccc6f3 | 736 | snprintf(dirname, sizeof(dirname), "%d", i); |
31253488 ZL |
737 | retval = ext2fs_mkdir(fs, parent, 0, dirname); |
738 | if (retval) { | |
739 | com_err("dir_test", retval, | |
740 | "while creating %s dir", dirname); | |
741 | return retval; | |
742 | } | |
743 | ||
744 | retval = ext2fs_lookup(fs, parent, dirname, strlen(dirname), | |
745 | 0, &tmp); | |
746 | if (retval) { | |
747 | com_err("dir_test", retval, | |
748 | "while looking up %s dir", parent_name); | |
749 | return retval; | |
750 | } | |
751 | ||
752 | if (dir != tmp) { | |
bbccc6f3 AD |
753 | fprintf(stderr, |
754 | "tst_inline_data: dir (%u) != tmp (%u)\n", | |
31253488 ZL |
755 | dir, tmp); |
756 | return 1; | |
757 | } | |
758 | } | |
759 | ||
bbccc6f3 | 760 | snprintf(dirname, sizeof(dirname), "%d", i); |
31253488 ZL |
761 | retval = ext2fs_mkdir(fs, parent, 0, dirname); |
762 | if (retval && retval != EXT2_ET_DIR_NO_SPACE) { | |
763 | com_err("dir_test", retval, "while creating %s dir", dirname); | |
764 | return retval; | |
765 | } | |
766 | ||
767 | retval = ext2fs_expand_dir(fs, parent); | |
768 | if (retval) { | |
769 | com_err("dir_test", retval, "while expanding %s dir", parent_name); | |
770 | return retval; | |
771 | } | |
772 | ||
773 | return 0; | |
774 | } | |
775 | ||
776 | int main(int argc, char *argv[]) | |
777 | { | |
778 | ext2_filsys fs; | |
779 | struct ext2_super_block param; | |
780 | errcode_t retval; | |
31253488 ZL |
781 | |
782 | /* setup */ | |
783 | initialize_ext2_error_table(); | |
784 | ||
785 | memset(¶m, 0, sizeof(param)); | |
786 | ext2fs_blocks_count_set(¶m, 32768); | |
787 | param.s_inodes_count = 100; | |
788 | ||
789 | param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_INLINE_DATA; | |
790 | param.s_rev_level = EXT2_DYNAMIC_REV; | |
791 | param.s_inode_size = 256; | |
792 | ||
793 | retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, | |
794 | test_io_manager, &fs); | |
795 | if (retval) { | |
796 | com_err("setup", retval, | |
797 | "while initializing filesystem"); | |
798 | exit(1); | |
799 | } | |
800 | ||
801 | retval = ext2fs_allocate_tables(fs); | |
802 | if (retval) { | |
803 | com_err("setup", retval, | |
ce20096f | 804 | "while allocating tables for test filesystem"); |
31253488 ZL |
805 | exit(1); |
806 | } | |
807 | ||
808 | /* initialize inode cache */ | |
809 | if (!fs->icache) { | |
31253488 ZL |
810 | ext2_ino_t first_ino = EXT2_FIRST_INO(fs->super); |
811 | int i; | |
812 | ||
813 | /* we just want to init inode cache. So ignore error */ | |
814 | ext2fs_create_inode_cache(fs, 16); | |
815 | if (!fs->icache) { | |
816 | fprintf(stderr, | |
817 | "tst_inline_data: init inode cache failed\n"); | |
818 | exit(1); | |
819 | } | |
820 | ||
821 | /* setup inode cache */ | |
822 | for (i = 0; i < fs->icache->cache_size; i++) | |
823 | fs->icache->cache[i].ino = first_ino++; | |
824 | } | |
825 | ||
826 | /* test */ | |
827 | if (file_test(fs)) { | |
828 | fprintf(stderr, "tst_inline_data(FILE): FAILED\n"); | |
829 | return 1; | |
830 | } | |
831 | printf("tst_inline_data(FILE): OK\n"); | |
832 | ||
833 | if (dir_test(fs)) { | |
834 | fprintf(stderr, "tst_inline_data(DIR): FAILED\n"); | |
835 | return 1; | |
836 | } | |
837 | printf("tst_inline_data(DIR): OK\n"); | |
151c49bc | 838 | ext2fs_free(fs); |
31253488 ZL |
839 | |
840 | return 0; | |
841 | } | |
842 | #endif |