From: Zheng Liu Subject: [PATCH 08/32] libext2fs: add inline_data file Date: Mon, 16 Apr 2012 19:39:43 +0800 Message-ID: <1334576407-4007-9-git-send-email-wenqing.lz@taobao.com> References: <1334576407-4007-1-git-send-email-wenqing.lz@taobao.com> Cc: Zheng Liu To: linux-ext4@vger.kernel.org Return-path: Received: from mail-pb0-f46.google.com ([209.85.160.46]:43674 "EHLO mail-pb0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753030Ab2DPLeG (ORCPT ); Mon, 16 Apr 2012 07:34:06 -0400 Received: by mail-pb0-f46.google.com with SMTP id un15so6233785pbc.19 for ; Mon, 16 Apr 2012 04:34:06 -0700 (PDT) In-Reply-To: <1334576407-4007-1-git-send-email-wenqing.lz@taobao.com> Sender: linux-ext4-owner@vger.kernel.org List-ID: From: Zheng Liu inline_data.c file is craeted to implement related functions to support inline_data feature. Signed-off-by: Zheng Liu --- lib/ext2fs/Makefile.in | 5 + lib/ext2fs/Makefile.pq | 1 + lib/ext2fs/ext2fs.h | 54 ++++ lib/ext2fs/inline_data.c | 715 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 775 insertions(+), 0 deletions(-) create mode 100644 lib/ext2fs/inline_data.c diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 507a459..af631ce 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -58,6 +58,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ ind_block.o \ initialize.o \ inline.o \ + inline_data.o \ inode.o \ io_manager.o \ ismounted.o \ @@ -130,6 +131,7 @@ SRCS= ext2_err.c \ $(srcdir)/ind_block.c \ $(srcdir)/initialize.c \ $(srcdir)/inline.c \ + $(srcdir)/inline_data.c \ $(srcdir)/inode.c \ $(srcdir)/inode_io.c \ $(srcdir)/imager.c \ @@ -727,6 +729,9 @@ inline.o: $(srcdir)/inline.c $(top_builddir)/lib/config.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h +inline_data.o: $(srcdir)/inline_data.c $(top_builddir)/lib/config.h \ + $(srcdir)/ext2_fs.h $(srcdir)/ext2fs.h $(srcdir)/ext2_ext_attr.h \ + $(srcdir)/ext2fsP.h inode.o: $(srcdir)/inode.c $(top_builddir)/lib/config.h \ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ diff --git a/lib/ext2fs/Makefile.pq b/lib/ext2fs/Makefile.pq index 2f7b654..89082a7 100644 --- a/lib/ext2fs/Makefile.pq +++ b/lib/ext2fs/Makefile.pq @@ -27,6 +27,7 @@ OBJS= alloc.obj \ icount.obj \ initialize.obj \ inline.obj \ + inline_data.obj \ inode.obj \ ismounted.obj \ link.obj \ diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index a6518be..cb1b58e 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -1266,6 +1266,60 @@ extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount); errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *); +/* inline_data.c */ +extern errcode_t ext2fs_inline_data_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_inline_data_iterate2(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_ino_t ino, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_inline_data_iterate3(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + const char *name, + int namelen, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_inline_data_search_dir(ext2_filsys fs, ext2_ino_t ino, + char *search_name, int len); +extern errcode_t ext2fs_inline_data_expand_dir(ext2_filsys fs, ext2_ino_t ino, + int flags, char *block_buf, + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data); +extern void *ext2fs_get_inline_xattr_pos(struct ext2_inode_large *inode, + struct inline_data *idata); +extern void ext2fs_iget_extra_inode(ext2_filsys fs, struct ext2_inode_large *inode, + struct inline_data *idata); +extern int ext2fs_has_inline_data(ext2_filsys fs, ext2_ino_t ino); +extern int ext2fs_inline_data_in_extra(ext2_filsys fs, ext2_ino_t ino); +extern int ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino); + /* inode.c */ extern errcode_t ext2fs_flush_icache(ext2_filsys fs); extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c new file mode 100644 index 0000000..4ebd592 --- /dev/null +++ b/lib/ext2fs/inline_data.c @@ -0,0 +1,715 @@ +/* + * 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 "ext2_fs.h" +#include "ext2_ext_attr.h" + +#include "ext2fs.h" +#include "ext2fsP.h" + +#define EXT4_XATTR_SYSTEM_DATA_INDEX 7 +#define EXT4_XATTR_SYSTEM_DATA_NAME "data" + +struct inline_data_context { + ext2_ino_t ino; + int flags; + char *buf; + int (*func)(ext2_ino_t ino, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *priv_data; + errcode_t errcode; +}; + +static void inline_data_update_dir_entry(ext2_filsys fs, void *de_buf, + int old_size, int new_size) +{ + struct ext2_dir_entry *entry, *prev; + void *limit; + unsigned int de_len; + + entry = (struct ext2_dir_entry *)de_buf; + if (old_size) { + limit = de_buf + old_size; + do { + prev = entry; + ext2fs_get_rec_len(fs, entry, &de_len); + de_buf += de_len; + entry = (struct ext2_dir_entry *)de_buf; + } while (de_buf < limit); + + ext2fs_set_rec_len(fs, de_len + new_size - old_size, prev); + } else { + entry->inode = 0; + ext2fs_set_rec_len(fs, new_size, entry); + } +} + +static int inline_data_destory_data(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode_large *inode, + struct inline_data *idata) +{ + struct ext2_ext_attr_entry *entry; + errcode_t retval; + char *start, *end; + int cmp; + __u32 *magic; + + if (!idata->inline_off) + return 0; + + if (inode->i_extra_isize > (EXT2_INODE_SIZE(fs->super) - + EXT2_GOOD_OLD_INODE_SIZE)) { + fprintf(stderr, "invalid inode->i_extra_isize (%u)\n", + inode->i_extra_isize); + return -1; + } + + magic = (__u32*)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize); + start = (char *)magic + sizeof(__u32); + end = (char *)inode + EXT2_INODE_SIZE(fs->super); + entry = (struct ext2_ext_attr_entry *)start; + + cmp = ext2fs_find_entry_ext_attr(&entry, EXT4_XATTR_SYSTEM_DATA_INDEX, + EXT4_XATTR_SYSTEM_DATA_NAME, + end - start, 0); + + if (!cmp) { + entry->e_value_offs = 0; + entry->e_value_block = 0; + entry->e_value_size = 0; + } + + memset((void *)inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE); + + if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { + if (LINUX_S_ISDIR(inode->i_mode) || + LINUX_S_ISREG(inode->i_mode) || + LINUX_S_ISLNK(inode->i_mode)) + inode->i_flags |= EXT4_EXTENTS_FL; + } + + inode->i_flags &= ~EXT4_INLINE_DATA_FL; + + retval = ext2fs_write_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + return retval; + + return 0; +} + +static int inline_data_search_dir(ext2_filsys fs, + struct ext2_inode_large *inode, + char *start, + int size, + void *priv_data) +{ + struct inline_data_context *ctx; + struct ext2_dir_entry *dirent; + unsigned int rec_len; + unsigned int offset = 0; + char *dlimit; + int ret; + int do_abort = 0; + int changed = 0; + + ctx = (struct inline_data_context *)priv_data; + + dirent = (struct ext2_dir_entry *)start; + dlimit = start + size; + + while ((char *)dirent < dlimit) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + ret = (ctx->func)(ctx->ino, 0, dirent, + offset, size, start, + ctx->priv_data); + + if (ret & DIRENT_CHANGED) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + changed++; + } + + if (ret & DIRENT_ABORT) + do_abort++; + +next: + dirent = (struct ext2_dir_entry *)((char *)dirent + rec_len); + offset += rec_len; + } + + if (changed) { + ctx->errcode = ext2fs_write_inode_full(fs, ctx->ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (ctx->errcode) + return BLOCK_ABORT; + } + + if (do_abort) + return BLOCK_ABORT; + + return 0; +} + +static int inline_data_search_dir2(ext2_filsys fs, char *start, int size, + char *search_name, int len) +{ + struct ext2_dir_entry *dirent; + unsigned int rec_len; + char *dlimit; + int ret; + int do_abort = 0; + + dirent = (struct ext2_dir_entry *)start; + dlimit = start + size; + + while ((char *)dirent < dlimit) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + if (dirent->inode && + len == (dirent->name_len & 0xFF) && + strncmp(search_name, dirent->name, len) == 0) { + printf("Entry found at inline data\n"); + break; + } + dirent = (struct ext2_dir_entry *)((char *)dirent + rec_len); + } + + return 0; +} + +void *ext2fs_get_inline_xattr_pos(struct ext2_inode_large *inode, + struct inline_data *idata) +{ + struct ext2_ext_attr_entry *entry; + struct ext2_ext_attr_ibody_header *header; + + header = (struct ext2_ext_attr_ibody_header *) + ((void *)inode + + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize); + entry = (struct ext2_ext_attr_entry *) + ((void *)inode + idata->inline_off); + + return (void *)((struct ext2_ext_attr_header *)((header) + 1)) + + entry->e_value_offs; +} + +void ext2fs_iget_extra_inode(ext2_filsys fs, struct ext2_inode_large *inode, + struct inline_data *idata) +{ + struct ext2_ext_attr_entry *entry; + char *start, *end; + int cmp; + __u32 *magic; + + idata->inline_off = 0; + if (inode->i_extra_isize > (EXT2_INODE_SIZE(fs->super) - + EXT2_GOOD_OLD_INODE_SIZE)) { + fprintf(stderr, "invalid inode->i_extra_isize (%u)\n", + inode->i_extra_isize); + return; + } + + magic = (__u32*)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize); + if (*magic == EXT2_EXT_ATTR_MAGIC) { + end = (char *)inode + EXT2_INODE_SIZE(fs->super); + start = (char *)magic + sizeof(__u32); + entry = (struct ext2_ext_attr_entry *)start; + + cmp = ext2fs_find_entry_ext_attr(&entry, EXT4_XATTR_SYSTEM_DATA_INDEX, + EXT4_XATTR_SYSTEM_DATA_NAME, + end - start, 0); + if (!cmp) { + idata->inline_off = (__u16)((void *)entry - + (void *)inode); + idata->inline_size = EXT4_MIN_INLINE_DATA_SIZE + + entry->e_value_size; + } + } +} + +int ext2fs_has_inline_data(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inode *inode; + errcode_t retval; + __u32 flags; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return 0; + + retval = ext2fs_read_inode_full(fs, ino, inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + return 0; + + flags = inode->i_flags; + ext2fs_free_mem(&inode); + + return (flags & EXT4_INLINE_DATA_FL); +} + +struct xlate { + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *real_private; +}; + +static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry EXT2FS_ATTR((unused)), + struct ext2_dir_entry *dirent, int offset, + int blocksize, char *buf, void *priv_data) +{ + struct xlate *xl = (struct xlate *)priv_data; + + return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); +} + +errcode_t ext2fs_inline_data_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_inline_data_iterate2(fs, ino, flags, block_buf, + xlate_func, &xl); +} + +errcode_t ext2fs_inline_data_iterate2(ext2_filsys fs, + ext2_ino_t ino, int flags, char *block_buf, + int (*func)(ext2_ino_t ino, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct inline_data_context ctx; + struct ext2_inode_large *inode; + struct inline_data idata; + errcode_t retval; + blk64_t blk64; + void *inline_start; + int inline_size; + int ret = 0; + int i, limit, r; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_check_directory(fs, ino); + if (retval) + return retval; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) { + ext2fs_free_mem(&inode); + return retval; + } + + ctx.ino = ino; + ctx.flags = flags; + ctx.buf = block_buf; + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + + inline_start = inode->i_block; + inline_size = EXT4_MIN_INLINE_DATA_SIZE; + ret = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx); + if (ret) + goto out; + + ext2fs_iget_extra_inode(fs, inode, &idata); + if (idata.inline_size > EXT4_MIN_INLINE_DATA_SIZE) { + inline_start = ext2fs_get_inline_xattr_pos(inode, &idata); + inline_size = idata.inline_size - EXT4_MIN_INLINE_DATA_SIZE; + ret = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx); + + if (ret) + goto out; + } + +out: + return ret & BLOCK_ABORT ? 0 : ret; +} + +errcode_t ext2fs_inline_data_iterate3(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + const char *name, + int namelen, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + struct inline_data_context ctx; + struct ext2_inode_large *inode; + struct inline_data idata; + ext2_extent_handle_t handle; + errcode_t retval; + blk64_t blk; + void *inline_start; + char *backup_buf; + char *blk_buf; + int inline_size; + int ret = 0; + int i, limit, r; + + xl.real_private = priv_data; + xl.func = func; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) { + ext2fs_free_mem(&inode); + return retval; + } + + ctx.ino = ino; + ctx.flags = flags; + ctx.buf = block_buf; + ctx.func = xlate_func; + ctx.priv_data = &xl; + ctx.errcode = 0; + + inline_start = inode->i_block; + inline_size = EXT4_MIN_INLINE_DATA_SIZE; + ret = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx); + if (ret) + goto out; + + ext2fs_iget_extra_inode(fs, inode, &idata); + inline_size = idata.inline_size - EXT4_MIN_INLINE_DATA_SIZE; + if (inline_size > 0) { + inline_start = ext2fs_get_inline_xattr_pos(inode, &idata); + ret = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx); + if (ret) + goto out; + } + + /* + * TODO: we should try to expand xattr space firstly. But + * when creating too many dirs, Tao got a performance regression. + * Thus, it will be discussed and re-designed. + */ + + /* + * The inline space is filled up. So create a new block for it. + * As the extent tree will be created, we have to save the inline + * dir first. + */ + inline_size = idata.inline_size; + retval = ext2fs_get_mem(inline_size, &backup_buf); + if (retval) { + ret = retval; + goto out; + } + + memcpy(backup_buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE); + if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) + memcpy(backup_buf + EXT4_MIN_INLINE_DATA_SIZE, + ext2fs_get_inline_xattr_pos(inode, &idata), + inline_size - EXT4_MIN_INLINE_DATA_SIZE); + + /* clear the entry and the flag in dir now */ + ret = inline_data_destory_data(fs, ino, inode, &idata); + if (ret) + goto out; + + retval = ext2fs_new_block2(fs, 0, 0, &blk); + if (retval) + goto out; + + ext2fs_iblk_set(fs, (void *)inode, 1); + if (!(fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)) + inode->i_block[0] = blk; + inode->i_size = fs->blocksize; + + retval = ext2fs_get_mem(fs->blocksize, &blk_buf); + if (retval) + goto out; + + memcpy(blk_buf, backup_buf, inline_size); + + /* set the final dir entry to cover the whole block */ + inline_data_update_dir_entry(fs, blk_buf, inline_size, fs->blocksize); + + retval = ext2fs_write_dir_block(fs, blk, blk_buf); + if (retval) + goto out; + retval = ext2fs_write_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + goto out; + + if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { + retval = ext2fs_extent_open2(fs, ino, (void *)inode, &handle); + if (retval) + goto out; + retval = ext2fs_extent_set_bmap(handle, 0, blk, 0); + ext2fs_extent_free(handle); + if (retval) + goto out; + } + + /* Update accouting */ + ext2fs_block_alloc_stats2(fs, blk, +1); + + /* try to add a new entry */ + retval = ext2fs_dir_iterate(fs, ino, DIRENT_FLAG_INCLUDE_EMPTY, + 0, func, priv_data); + + ext2fs_free_mem(&backup_buf); + ext2fs_free_mem(&blk_buf); +out: + ext2fs_free_mem(&inode); + return ret & BLOCK_ABORT ? 0 : ret; +} + +errcode_t ext2fs_inline_data_search_dir(ext2_filsys fs, ext2_ino_t ino, + char *search_name, int len) +{ + struct ext2_inode_large *inode; + struct ext2_dir_entry *entry; + struct inline_data idata; + unsigned int rec_len; + errcode_t retval; + char *dlimit; + void *start; + int size; + int ret = 0; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + return retval; + + if (!(inode->i_flags & EXT4_INLINE_DATA_FL)) + goto out; + + start = inode->i_block; + size = EXT4_MIN_INLINE_DATA_SIZE; + ret = inline_data_search_dir2(fs, start, size, search_name, len); + if (!ret) + goto out; + + ret = 0; + ext2fs_iget_extra_inode(fs, inode, &idata); + if (idata.inline_size == EXT4_MIN_INLINE_DATA_SIZE) + goto out; + + start = ext2fs_get_inline_xattr_pos(inode, &idata); + size = idata.inline_size - EXT4_MIN_INLINE_DATA_SIZE; + ret = inline_data_search_dir2(fs, start, size, search_name, len); + +out: + ext2fs_free_mem(&inode); + return ret; +} + +errcode_t ext2fs_inline_data_header_verify(ext2_filsys fs, + struct ext2_inode *inode) +{ + struct inline_data idata; + int ret = 0; + + ext2fs_iget_extra_inode(fs, (void *)inode, &idata); + if (idata.inline_off == 0) + ret = -1; + + return ret; +} + +int ext2fs_inline_data_in_extra(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inode *inode; + struct inline_data idata; + + ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + ext2fs_read_inode_full(fs, ino, inode, EXT2_INODE_SIZE(fs->super)); + + ext2fs_iget_extra_inode(fs, inode, &idata); + ext2fs_free_mem(&inode); + if (idata.inline_off == 0 || + idata.inline_size == EXT4_MIN_INLINE_DATA_SIZE) + return 0; + else + return 1; +} + +int ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inode *inode; + struct inline_data idata; + + inode = (struct ext2_inode *) malloc(EXT2_INODE_SIZE(fs->super)); + ext2fs_read_inode_full(fs, ino, inode, EXT2_INODE_SIZE(fs->super)); + + ext2fs_iget_extra_inode(fs, inode, &idata); + free(inode); + if (idata.inline_off == 0) + return 0; + else + return idata.inline_size; +} + +errcode_t ext2fs_inline_data_expand_dir(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data) +{ + struct ext2_inode_large *inode; + struct inline_data idata; + ext2_extent_handle_t handle; + errcode_t retval; + blk64_t blk; + void *inline_start; + char *backup_buf; + char *blk_buf; + int inline_size; + int ret = 0; + int i, limit, r; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) { + ext2fs_free_mem(&inode); + return retval; + } + + ext2fs_iget_extra_inode(fs, inode, &idata); + + inline_size = idata.inline_size; + retval = ext2fs_get_mem(inline_size, &backup_buf); + if (retval) { + ret = retval; + goto out; + } + + memcpy(backup_buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE); + if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) + memcpy(backup_buf + EXT4_MIN_INLINE_DATA_SIZE, + ext2fs_get_inline_xattr_pos(inode, &idata), + inline_size - EXT4_MIN_INLINE_DATA_SIZE); + + /* clear the entry and the flag in dir now */ + ret = inline_data_destory_data(fs, ino, inode, &idata); + if (ret) + goto out; + + retval = ext2fs_new_block2(fs, 0, 0, &blk); + if (retval) + goto out; + + ext2fs_iblk_set(fs, (void *)inode, 1); + if (!(fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)) + inode->i_block[0] = blk; + inode->i_size = fs->blocksize; + + retval = ext2fs_get_mem(fs->blocksize, &blk_buf); + if (retval) + goto out; + + memcpy(blk_buf, backup_buf, inline_size); + + /* set the final dir entry to cover the whole block */ + inline_data_update_dir_entry(fs, blk_buf, inline_size, fs->blocksize); + + retval = ext2fs_write_dir_block(fs, blk, blk_buf); + if (retval) + goto out; + retval = ext2fs_write_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + goto out; + + if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { + retval = ext2fs_extent_open2(fs, ino, (void *)inode, &handle); + if (retval) + goto out; + retval = ext2fs_extent_set_bmap(handle, 0, blk, 0); + ext2fs_extent_free(handle); + if (retval) + goto out; + } + + /* Update accouting */ + ext2fs_block_alloc_stats2(fs, blk, +1); + + /* try to add a new entry */ + retval = ext2fs_block_iterate3(fs, ino, flags, 0, func, priv_data); + + ext2fs_free_mem(&backup_buf); + ext2fs_free_mem(&blk_buf); +out: + ext2fs_free_mem(&inode); + return ret & BLOCK_ABORT ? 0 : ret; +} -- 1.7.4.1