From 464df1d5e5ab1322e2dd0a7796939fff1aeefa9a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:49:25 +0200 Subject: Adding upstream version 1.47.0. Signed-off-by: Daniel Baumann --- lib/ext2fs/inline_data.c | 842 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 842 insertions(+) create mode 100644 lib/ext2fs/inline_data.c (limited to 'lib/ext2fs/inline_data.c') diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c new file mode 100644 index 0000000..bd52e37 --- /dev/null +++ b/lib/ext2fs/inline_data.c @@ -0,0 +1,842 @@ +/* + * inline_data.c --- data in inode + * + * Copyright (C) 2012 Zheng Liu + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU library + * General Public License, version 2. + * %End-Header% + */ + +#include "config.h" +#include +#include +#include /* for PATH_MAX */ + +#include "ext2_fs.h" +#include "ext2_ext_attr.h" + +#include "ext2fs.h" +#include "ext2fsP.h" + +struct ext2_inline_data { + ext2_filsys fs; + ext2_ino_t ino; + size_t ea_size; /* the size of inline data in ea area */ + void *ea_data; +}; + +static errcode_t ext2fs_inline_data_ea_set(struct ext2_inline_data *data) +{ + struct ext2_xattr_handle *handle; + errcode_t retval; + + retval = ext2fs_xattrs_open(data->fs, data->ino, &handle); + if (retval) + return retval; + + retval = ext2fs_xattrs_read(handle); + if (retval) + goto err; + + retval = ext2fs_xattr_set(handle, "system.data", + data->ea_data, data->ea_size); +err: + (void) ext2fs_xattrs_close(&handle); + return retval; +} + +static errcode_t ext2fs_inline_data_ea_get(struct ext2_inline_data *data) +{ + struct ext2_xattr_handle *handle; + errcode_t retval; + + data->ea_size = 0; + data->ea_data = 0; + + retval = ext2fs_xattrs_open(data->fs, data->ino, &handle); + if (retval) + return retval; + + retval = ext2fs_xattrs_read(handle); + if (retval) + goto err; + + retval = ext2fs_xattr_get(handle, "system.data", + (void **)&data->ea_data, &data->ea_size); + if (retval == EXT2_ET_EA_KEY_NOT_FOUND) { + data->ea_size = 0; + data->ea_data = NULL; + retval = 0; + } else if (retval) + goto err; + +err: + (void) ext2fs_xattrs_close(&handle); + return retval; +} + +errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inline_data data; + char empty[1] = { '\0' }; + + data.fs = fs; + data.ino = ino; + data.ea_size = 0; + data.ea_data = empty; + return ext2fs_inline_data_ea_set(&data); +} + +errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino, size_t *size) +{ + struct ext2_inode inode; + struct ext2_inline_data data; + errcode_t retval; + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + + if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) + return EXT2_ET_NO_INLINE_DATA; + + data.fs = fs; + data.ino = ino; + retval = ext2fs_inline_data_ea_get(&data); + if (retval) + return retval; + + *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size; + return ext2fs_free_mem(&data.ea_data); +} + +int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, + void *priv_data) +{ + struct dir_context *ctx; + struct ext2_inode inode; + struct ext2_dir_entry dirent; + struct ext2_inline_data data; + int ret = BLOCK_ABORT; + e2_blkcnt_t blockcnt = 0; + char *old_buf; + unsigned int old_buflen; + int old_flags; + + ctx = (struct dir_context *)priv_data; + old_buf = ctx->buf; + old_buflen = ctx->buflen; + old_flags = ctx->flags; + ctx->flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA; + + ctx->errcode = ext2fs_read_inode(fs, ino, &inode); + if (ctx->errcode) + goto out; + + if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) { + ctx->errcode = EXT2_ET_NO_INLINE_DATA; + goto out; + } + + if (!LINUX_S_ISDIR(inode.i_mode)) { + ctx->errcode = EXT2_ET_NO_DIRECTORY; + goto out; + } + ret = 0; + + /* we first check '.' and '..' dir */ + dirent.inode = ino; + dirent.name_len = 1; + ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent); + dirent.name[0] = '.'; + dirent.name[1] = '\0'; + ctx->buf = (char *)&dirent; + ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_ABORT) + goto out; + + dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]); + dirent.name_len = 2; + ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent); + dirent.name[0] = '.'; + dirent.name[1] = '.'; + dirent.name[2] = '\0'; + ctx->buf = (char *)&dirent; + ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_INLINE_DATA_CHANGED) { + errcode_t err; + + inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode); + err = ext2fs_write_inode(fs, ino, &inode); + if (err) + goto out; + ret &= ~BLOCK_INLINE_DATA_CHANGED; + } + if (ret & BLOCK_ABORT) + goto out; + + ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE; + ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE; +#ifdef WORDS_BIGENDIAN + ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out; + } +#endif + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_INLINE_DATA_CHANGED) { +#ifdef WORDS_BIGENDIAN + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, + ctx->buflen, 0); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out; + } +#endif + ctx->errcode = ext2fs_write_inode(fs, ino, &inode); + if (ctx->errcode) + ret |= BLOCK_ABORT; + ret &= ~BLOCK_INLINE_DATA_CHANGED; + } + if (ret & BLOCK_ABORT) + goto out; + + data.fs = fs; + data.ino = ino; + ctx->errcode = ext2fs_inline_data_ea_get(&data); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out; + } + if (data.ea_size <= 0) + goto out1; + + ctx->buf = data.ea_data; + ctx->buflen = data.ea_size; +#ifdef WORDS_BIGENDIAN + ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out1; + } +#endif + + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_INLINE_DATA_CHANGED) { +#ifdef WORDS_BIGENDIAN + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, + ctx->buflen, 0); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out1; + } +#endif + ctx->errcode = ext2fs_inline_data_ea_set(&data); + if (ctx->errcode) + ret |= BLOCK_ABORT; + } + +out1: + ext2fs_free_mem(&data.ea_data); +out: + ctx->buf = old_buf; + ctx->buflen = old_buflen; + ctx->flags = old_flags; + ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED); + return ret; +} + +errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_xattr_handle *handle; + errcode_t retval; + + retval = ext2fs_xattrs_open(fs, ino, &handle); + if (retval) + return retval; + + retval = ext2fs_xattrs_read(handle); + if (retval) + goto err; + + retval = ext2fs_xattr_remove(handle, "system.data"); +err: + (void) ext2fs_xattrs_close(&handle); + return retval; +} + +static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino, + char *bbuf, char *ibuf, int size) +{ + struct ext2_dir_entry *dir, *dir2; + struct ext2_dir_entry_tail *t; + errcode_t retval; + int offset; + unsigned int rec_len; + int csum_size = 0; + int filetype = 0; + + if (ext2fs_has_feature_metadata_csum(fs->super)) + csum_size = sizeof(struct ext2_dir_entry_tail); + + /* Create '.' and '..' */ + if (ext2fs_has_feature_filetype(fs->super)) + filetype = EXT2_FT_DIR; + + /* + * Set up entry for '.' + */ + dir = (struct ext2_dir_entry *) bbuf; + dir->inode = ino; + ext2fs_dirent_set_name_len(dir, 1); + ext2fs_dirent_set_file_type(dir, filetype); + dir->name[0] = '.'; + rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); + dir->rec_len = EXT2_DIR_REC_LEN(1); + + /* + * Set up entry for '..' + */ + dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len); + dir->rec_len = EXT2_DIR_REC_LEN(2); + dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]); + ext2fs_dirent_set_name_len(dir, 2); + ext2fs_dirent_set_file_type(dir, filetype); + dir->name[0] = '.'; + dir->name[1] = '.'; + + /* + * Adjust the last rec_len + */ + offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2); + dir = (struct ext2_dir_entry *) (bbuf + offset); + memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE, + size - EXT4_INLINE_DATA_DOTDOT_SIZE); + size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) - + EXT4_INLINE_DATA_DOTDOT_SIZE; + + do { + dir2 = dir; + retval = ext2fs_get_rec_len(fs, dir, &rec_len); + if (retval) + goto err; + offset += rec_len; + dir = (struct ext2_dir_entry *) (bbuf + offset); + } while (offset < size); + rec_len += fs->blocksize - csum_size - offset; + retval = ext2fs_set_rec_len(fs, rec_len, dir2); + if (retval) + goto err; + + if (csum_size) { + t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize); + ext2fs_initialize_dirent_tail(fs, t); + } + +err: + return retval; +} + +static errcode_t +ext2fs_inline_data_dir_expand(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, char *buf, size_t size) +{ + errcode_t retval; + blk64_t blk; + char *blk_buf; + + retval = ext2fs_get_memzero(fs->blocksize, &blk_buf); + if (retval) + return retval; + +#ifdef WORDS_BIGENDIAN + retval = ext2fs_dirent_swab_in2(fs, buf + EXT4_INLINE_DATA_DOTDOT_SIZE, + size, 0); + if (retval) + goto errout; +#endif + + /* Adjust the rec_len */ + retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, buf, size); + if (retval) + goto errout; + /* Allocate a new block */ + retval = ext2fs_new_block2(fs, 0, 0, &blk); + if (retval) + goto errout; + retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino); + if (retval) + goto errout; + + /* Update inode */ + if (ext2fs_has_feature_extents(fs->super)) + inode->i_flags |= EXT4_EXTENTS_FL; + inode->i_flags &= ~EXT4_INLINE_DATA_FL; + retval = ext2fs_iblk_add_blocks(fs, inode, 1); + if (retval) + goto errout; + inode->i_size = fs->blocksize; + retval = ext2fs_bmap2(fs, ino, inode, 0, BMAP_SET, 0, 0, &blk); + if (retval) + goto errout; + retval = ext2fs_write_inode(fs, ino, inode); + if (retval) + goto errout; + ext2fs_block_alloc_stats(fs, blk, +1); + +errout: + ext2fs_free_mem(&blk_buf); + return retval; +} + +static errcode_t +ext2fs_inline_data_file_expand(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, char *buf, size_t size) +{ + ext2_file_t e2_file; + errcode_t retval; + + /* Update inode */ + memset(inode->i_block, 0, sizeof(inode->i_block)); + if (ext2fs_has_feature_extents(fs->super)) { + ext2_extent_handle_t handle; + + inode->i_flags &= ~EXT4_EXTENTS_FL; + retval = ext2fs_extent_open2(fs, ino, inode, &handle); + if (retval) + return retval; + ext2fs_extent_free(handle); + } + inode->i_flags &= ~EXT4_INLINE_DATA_FL; + inode->i_size = 0; + retval = ext2fs_write_inode(fs, ino, inode); + if (retval) + return retval; + + /* Write out the block buffer */ + retval = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file); + if (retval) + return retval; + retval = ext2fs_file_write(e2_file, buf, size, 0); + ext2fs_file_close(e2_file); + return retval; +} + +errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inode inode; + struct ext2_inline_data data; + errcode_t retval; + size_t inline_size; + char *inline_buf = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + + if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) + return EXT2_ET_NO_INLINE_DATA; + + data.fs = fs; + data.ino = ino; + retval = ext2fs_inline_data_ea_get(&data); + if (retval) + return retval; + inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE; + retval = ext2fs_get_mem(inline_size, &inline_buf); + if (retval) + goto errout; + + memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE); + if (data.ea_size > 0) { + memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE, + data.ea_data, data.ea_size); + } + + memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE); + /* + * NOTE: We must do this write -> ea_remove -> read cycle here because + * removing the inline data EA can free the EA block, which is a change + * that our stack copy of the inode will never see. If that happens, + * we can end up with the EA block and lblk 0 pointing to the same + * pblk, which is bad news. + */ + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) + goto errout; + retval = ext2fs_inline_data_ea_remove(fs, ino); + if (retval) + goto errout; + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + goto errout; + + if (LINUX_S_ISDIR(inode.i_mode)) { + retval = ext2fs_inline_data_dir_expand(fs, ino, &inode, + inline_buf, inline_size); + } else { + retval = ext2fs_inline_data_file_expand(fs, ino, &inode, + inline_buf, inline_size); + } + +errout: + if (inline_buf) + ext2fs_free_mem(&inline_buf); + ext2fs_free_mem(&data.ea_data); + return retval; +} + +/* + * When caller uses this function to retrieve the inline data, it must + * allocate a buffer which has the size of inline data. The size of + * inline data can be know by ext2fs_inline_data_get_size(). + */ +errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + void *buf, size_t *size) +{ + struct ext2_inode inode_buf; + struct ext2_inline_data data; + errcode_t retval; + + if (!inode) { + retval = ext2fs_read_inode(fs, ino, &inode_buf); + if (retval) + return retval; + inode = &inode_buf; + } + + data.fs = fs; + data.ino = ino; + retval = ext2fs_inline_data_ea_get(&data); + if (retval) + return retval; + + memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE); + if (data.ea_size > 0) + memcpy((char *) buf + EXT4_MIN_INLINE_DATA_SIZE, + data.ea_data, data.ea_size); + + if (size) + *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size; + ext2fs_free_mem(&data.ea_data); + return 0; +} + +errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + void *buf, size_t size) +{ + struct ext2_inode inode_buf; + struct ext2_inline_data data = { + .fs = fs, + .ino = ino, + }; + errcode_t retval; + size_t free_ea_size, existing_size, free_inode_size; + + if (!inode) { + retval = ext2fs_read_inode(fs, ino, &inode_buf); + if (retval) + return retval; + inode = &inode_buf; + } + + if (size <= EXT4_MIN_INLINE_DATA_SIZE) { + memcpy((void *)inode->i_block, buf, size); + } else { + retval = ext2fs_xattr_inode_max_size(fs, ino, &free_ea_size); + if (retval) + return retval; + + retval = ext2fs_inline_data_size(fs, ino, &existing_size); + if (retval) + return retval; + + if (existing_size < EXT4_MIN_INLINE_DATA_SIZE) { + free_inode_size = EXT4_MIN_INLINE_DATA_SIZE - + existing_size; + } else { + free_inode_size = 0; + } + + if (size != existing_size && + size > existing_size + free_ea_size + free_inode_size) + return EXT2_ET_INLINE_DATA_NO_SPACE; + + memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE); + if (size > EXT4_MIN_INLINE_DATA_SIZE) + data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE; + data.ea_data = (char *) buf + EXT4_MIN_INLINE_DATA_SIZE; + } + retval = ext2fs_write_inode(fs, ino, inode); + if (retval) + return retval; + return ext2fs_inline_data_ea_set(&data); +} + +#ifdef DEBUG +#include "e2p/e2p.h" + +/* + * The length of buffer is set to 64 because in inode's i_block member it only + * can save 60 bytes. Thus this value can let the data being saved in extra + * space. + */ +#define BUFF_SIZE (64) + +static errcode_t file_test(ext2_filsys fs) +{ + struct ext2_inode inode; + ext2_ino_t newfile; + errcode_t retval; + size_t size; + char *buf = 0, *cmpbuf = 0; + + /* create a new file */ + retval = ext2fs_new_inode(fs, 2, 010755, 0, &newfile); + if (retval) { + com_err("file_test", retval, "while allocating a new inode"); + return 1; + } + + memset(&inode, 0, sizeof(inode)); + inode.i_flags |= EXT4_INLINE_DATA_FL; + inode.i_size = EXT4_MIN_INLINE_DATA_SIZE; + inode.i_mode = LINUX_S_IFREG; + retval = ext2fs_write_new_inode(fs, newfile, &inode); + if (retval) { + com_err("file_test", retval, "while writing a new inode"); + return 1; + } + + retval = ext2fs_inline_data_init(fs, newfile); + if (retval) { + com_err("file_test", retval, "while init 'system.data'"); + return 1; + } + + retval = ext2fs_inline_data_size(fs, newfile, &size); + if (retval) { + com_err("file_test", retval, "while getting size"); + return 1; + } + + if (size != EXT4_MIN_INLINE_DATA_SIZE) { + fprintf(stderr, + "tst_inline_data: size of inline data is wrong\n"); + return 1; + } + + ext2fs_get_mem(BUFF_SIZE, &buf); + memset(buf, 'a', BUFF_SIZE); + retval = ext2fs_inline_data_set(fs, newfile, 0, buf, BUFF_SIZE); + if (retval) { + com_err("file_test", retval, + "while setting inline data %s", buf); + goto err; + } + + ext2fs_get_mem(BUFF_SIZE, &cmpbuf); + retval = ext2fs_inline_data_get(fs, newfile, 0, cmpbuf, &size); + if (retval) { + com_err("file_test", retval, "while getting inline data"); + goto err; + } + + if (size != BUFF_SIZE) { + fprintf(stderr, + "tst_inline_data: size %zu != buflen %u\n", + size, BUFF_SIZE); + retval = 1; + goto err; + } + + if (memcmp(buf, cmpbuf, BUFF_SIZE)) { + fprintf(stderr, "tst_inline_data: buf != cmpbuf\n"); + retval = 1; + goto err; + } + + retval = ext2fs_punch(fs, newfile, 0, 0, 0, ~0ULL); + if (retval) { + com_err("file_test", retval, "while truncating inode"); + goto err; + } + + /* reload inode and check isize */ + ext2fs_read_inode(fs, newfile, &inode); + if (inode.i_size != 0) { + fprintf(stderr, "tst_inline_data: i_size should be 0\n"); + retval = 1; + } + +err: + if (cmpbuf) + ext2fs_free_mem(&cmpbuf); + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +static errcode_t dir_test(ext2_filsys fs) +{ + const char *dot_name = "."; + const char *stub_name = "stub"; + const char *parent_name = "test"; + ext2_ino_t parent, dir, tmp; + errcode_t retval; + char dirname[32]; + int i; + + retval = ext2fs_mkdir(fs, 11, 11, stub_name); + if (retval) { + com_err("dir_test", retval, "while creating %s dir", stub_name); + return retval; + } + + retval = ext2fs_mkdir(fs, 11, 0, parent_name); + if (retval) { + com_err("dir_test", retval, + "while creating %s dir", parent_name); + return retval; + } + + retval = ext2fs_lookup(fs, 11, parent_name, strlen(parent_name), + 0, &parent); + if (retval) { + com_err("dir_test", retval, + "while looking up %s dir", parent_name); + return retval; + } + + retval = ext2fs_lookup(fs, parent, dot_name, strlen(dot_name), + 0, &tmp); + if (retval) { + com_err("dir_test", retval, + "while looking up %s dir", parent_name); + return retval; + } + + if (parent != tmp) { + fprintf(stderr, "tst_inline_data: parent (%u) != tmp (%u)\n", + parent, tmp); + return 1; + } + + for (i = 0, dir = 13; i < 4; i++, dir++) { + tmp = 0; + snprintf(dirname, sizeof(dirname), "%d", i); + retval = ext2fs_mkdir(fs, parent, 0, dirname); + if (retval) { + com_err("dir_test", retval, + "while creating %s dir", dirname); + return retval; + } + + retval = ext2fs_lookup(fs, parent, dirname, strlen(dirname), + 0, &tmp); + if (retval) { + com_err("dir_test", retval, + "while looking up %s dir", parent_name); + return retval; + } + + if (dir != tmp) { + fprintf(stderr, + "tst_inline_data: dir (%u) != tmp (%u)\n", + dir, tmp); + return 1; + } + } + + snprintf(dirname, sizeof(dirname), "%d", i); + retval = ext2fs_mkdir(fs, parent, 0, dirname); + if (retval && retval != EXT2_ET_DIR_NO_SPACE) { + com_err("dir_test", retval, "while creating %s dir", dirname); + return retval; + } + + retval = ext2fs_expand_dir(fs, parent); + if (retval) { + com_err("dir_test", retval, "while expanding %s dir", parent_name); + return retval; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + ext2_filsys fs; + struct ext2_super_block param; + errcode_t retval; + + /* setup */ + initialize_ext2_error_table(); + + memset(¶m, 0, sizeof(param)); + ext2fs_blocks_count_set(¶m, 32768); + param.s_inodes_count = 100; + + param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_INLINE_DATA; + param.s_rev_level = EXT2_DYNAMIC_REV; + param.s_inode_size = 256; + + retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, + test_io_manager, &fs); + if (retval) { + com_err("setup", retval, + "while initializing filesystem"); + exit(1); + } + + retval = ext2fs_allocate_tables(fs); + if (retval) { + com_err("setup", retval, + "while allocating tables for test filesystem"); + exit(1); + } + + /* initialize inode cache */ + if (!fs->icache) { + ext2_ino_t first_ino = EXT2_FIRST_INO(fs->super); + int i; + + /* we just want to init inode cache. So ignore error */ + ext2fs_create_inode_cache(fs, 16); + if (!fs->icache) { + fprintf(stderr, + "tst_inline_data: init inode cache failed\n"); + exit(1); + } + + /* setup inode cache */ + for (i = 0; i < fs->icache->cache_size; i++) + fs->icache->cache[i].ino = first_ino++; + } + + /* test */ + if (file_test(fs)) { + fprintf(stderr, "tst_inline_data(FILE): FAILED\n"); + return 1; + } + printf("tst_inline_data(FILE): OK\n"); + + if (dir_test(fs)) { + fprintf(stderr, "tst_inline_data(DIR): FAILED\n"); + return 1; + } + printf("tst_inline_data(DIR): OK\n"); + ext2fs_free(fs); + + return 0; +} +#endif -- cgit v1.2.3