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