Home | History | Annotate | Download | only in ext2fs
      1 /*
      2  * inline_data.c --- data in inode
      3  *
      4  * Copyright (C) 2012 Zheng Liu <wenqing.lz (at) 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 
     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 
     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 
     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 
     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 
    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 
    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 
    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
    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
    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 
    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  */
    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 
    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 
    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 
    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 
    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