Patchwork [08/32] libext2fs: add inline_data file

login
register
mail settings
Submitter Zheng Liu
Date April 16, 2012, 11:39 a.m.
Message ID <1334576407-4007-9-git-send-email-wenqing.lz@taobao.com>
Download mbox | patch
Permalink /patch/152792/
State Superseded
Headers show

Comments

Zheng Liu - April 16, 2012, 11:39 a.m.
From: Zheng Liu <wenqing.lz@taobao.com>

inline_data.c file is craeted to implement related functions to support
inline_data feature.

Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
---
 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

Patch

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 <wenqing.lz@taobao.com>
+ *
+ * %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 <stdio.h>
+
+#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;
+}