Patchwork [07/36,v4] libext2fs: add inline_data file

login
register
mail settings
Submitter Zheng Liu
Date July 31, 2012, 11:48 a.m.
Message ID <1343735309-30579-8-git-send-email-wenqing.lz@taobao.com>
Download mbox | patch
Permalink /patch/174224/
State Superseded
Headers show

Comments

Zheng Liu - July 31, 2012, 11:48 a.m.
From: Zheng Liu <wenqing.lz@taobao.com>

inline_data.c file is created 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      |   57 ++++
 lib/ext2fs/inline_data.c |  724 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 787 insertions(+), 0 deletions(-)
 create mode 100644 lib/ext2fs/inline_data.c
Theodore Ts'o - Aug. 7, 2012, 6:40 p.m.
One major comment about the library functions.  The reason why we have
functions of the form ext2fs_foo(), ext2fs_foo2(), ext2fs_foo3(),
etc. is to preserve backwards compatibility of the ABI.  In general,
ext2fs_foo2() will have an extra parameter which wasn't in
ext2fs_foo(), and ext2fs_foo2() will have a superset of the
functionality of ext2fs_foo().  If later we need to add to add another
parameter to further extend the functionality of the function, we
might have an ext2fs_foo3(), which again will be a supserset of the
functionality of ext2fs_foo2().   

So in general, we only need to implement ext2fs_fooN(), and then
ext2fs_fooX where 0 <= X <= N simply call ext2fs_fooN with the extra
parameters defaulted out (usually to 0 or NULL).

It also follows that when we create a new function, there's no need to
do this.  So the fact that you have an ext2fs_inlinedata_iterate()
which just calls ext2fs_inline_data_iterate2() is not something you
need to do.

More seriously, ext2fs_inline_data_iterate3() has an implementation
which is completely different from that of
ext2fs_inline_data_iterate2(), and that's an immediate red flag.  This
means that these two functions are semantically different, and so they
should have fundamentally different names --- or you need to make
ext2fs_inline_data_iterate3() a strict superset of the functionality
of ext2fs_inline_data_iterate2().

Also, I note that there are a number of patches which basically do
this:

-	retval = ext2fs_dir_iterate2(current_fs, inode_num, 0,
-				    0, rmdir_proc, &rds);
+	if (ext2fs_has_inline_data(current_fs, inode_num))
+		retval = ext2fs_inline_data_iterate2(current_fs, inode_num, 0,
+						     0, rmdir_proc, &rds);
+	else
+		retval = ext2fs_dir_iterate2(current_fs, inode_num, 0,
+					    0, rmdir_proc, &rds);

The much better thing to do is to make ext2fs_dir_iterate2() check to
see if the inode has inline data, and if so, to call
ext2fs_inline_data_iterate2() directly.   This has a couple of benefits.

The first is it will reduce the number of patches we need to apply.
More importantly, it means that existing programs that don't know
about inline data, but who happen to use ext2fs_dir_iterate(), will be
able to work automatically, without requiring code changes.  It's also
much cleaner from a conceptual point of view, since the
ext2fs_dir_iterate abstraction shouldn't need to expose to the user
whether the directory data is inline or in a separate data block.

Regards,

						- Ted
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Zheng Liu - Aug. 13, 2012, 8:07 a.m.
On Tue, Aug 07, 2012 at 02:40:02PM -0400, Theodore Ts'o wrote:
> One major comment about the library functions.  The reason why we have
> functions of the form ext2fs_foo(), ext2fs_foo2(), ext2fs_foo3(),
> etc. is to preserve backwards compatibility of the ABI.  In general,
> ext2fs_foo2() will have an extra parameter which wasn't in
> ext2fs_foo(), and ext2fs_foo2() will have a superset of the
> functionality of ext2fs_foo().  If later we need to add to add another
> parameter to further extend the functionality of the function, we
> might have an ext2fs_foo3(), which again will be a supserset of the
> functionality of ext2fs_foo2().   
> 
> So in general, we only need to implement ext2fs_fooN(), and then
> ext2fs_fooX where 0 <= X <= N simply call ext2fs_fooN with the extra
> parameters defaulted out (usually to 0 or NULL).
> 
> It also follows that when we create a new function, there's no need to
> do this.  So the fact that you have an ext2fs_inlinedata_iterate()
> which just calls ext2fs_inline_data_iterate2() is not something you
> need to do.
> 
> More seriously, ext2fs_inline_data_iterate3() has an implementation
> which is completely different from that of
> ext2fs_inline_data_iterate2(), and that's an immediate red flag.  This
> means that these two functions are semantically different, and so they
> should have fundamentally different names --- or you need to make
> ext2fs_inline_data_iterate3() a strict superset of the functionality
> of ext2fs_inline_data_iterate2().
> 
> Also, I note that there are a number of patches which basically do
> this:
> 
> -	retval = ext2fs_dir_iterate2(current_fs, inode_num, 0,
> -				    0, rmdir_proc, &rds);
> +	if (ext2fs_has_inline_data(current_fs, inode_num))
> +		retval = ext2fs_inline_data_iterate2(current_fs, inode_num, 0,
> +						     0, rmdir_proc, &rds);
> +	else
> +		retval = ext2fs_dir_iterate2(current_fs, inode_num, 0,
> +					    0, rmdir_proc, &rds);
> 
> The much better thing to do is to make ext2fs_dir_iterate2() check to
> see if the inode has inline data, and if so, to call
> ext2fs_inline_data_iterate2() directly.   This has a couple of benefits.
> 
> The first is it will reduce the number of patches we need to apply.
> More importantly, it means that existing programs that don't know
> about inline data, but who happen to use ext2fs_dir_iterate(), will be
> able to work automatically, without requiring code changes.  It's also
> much cleaner from a conceptual point of view, since the
> ext2fs_dir_iterate abstraction shouldn't need to expose to the user
> whether the directory data is inline or in a separate data block.

Thanks for patient explanation.  I will fix the patches according to
your advice.

Regards,
Zheng
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index f9200fa..5b4ec15 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 \
@@ -734,6 +736,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 fd051b1..bacc14d 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1329,6 +1329,63 @@  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);
+extern int inline_data_destory_data(ext2_filsys fs, ext2_ino_t ino,
+				    struct ext2_inode_large *inode,
+				    struct inline_data *idata);
+
 /* 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..59483a6
--- /dev/null
+++ b/lib/ext2fs/inline_data.c
@@ -0,0 +1,724 @@ 
+/*
+ * 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;
+	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);
+}
+
+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);
+
+	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 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;
+	}
+
+	if (inode->i_size == 0)
+		goto out;
+
+	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;
+	retval = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx);
+	if (retval)
+		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;
+		retval = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx);
+	}
+
+out:
+	ext2fs_free_mem(&inode);
+	return retval & BLOCK_ABORT ? 0 : retval;
+}
+
+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;
+	struct ext2_dir_entry_tail *t;
+	ext2_extent_handle_t handle;
+	errcode_t retval;
+	blk64_t blk;
+	void *inline_start;
+	char *backup_buf;
+	char *blk_buf;
+	int inline_size;
+	int i, limit, r;
+	int csum_size = 0;
+
+	xl.real_private = priv_data;
+	xl.func = func;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		csum_size = sizeof(struct ext2_dir_entry_tail);
+
+	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;
+	retval = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx);
+	if (retval)
+		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);
+		retval = inline_data_search_dir(fs, inode, inline_start, inline_size, &ctx);
+		if (retval)
+			goto out;
+	}
+
+	/*
+	 * The inline space is filled up. So create a new block for it.
+	 * As the extent tree se created, we have to save the inline
+	 * dir first.
+	 */
+	inline_size = idata.inline_size;
+	retval = ext2fs_get_mem(inline_size, &backup_buf);
+	if (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 */
+	retval = inline_data_destory_data(fs, ino, inode, &idata);
+	if (retval)
+		goto out;
+
+	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_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 - csum_size);
+
+	if (csum_size) {
+		t = EXT2_DIRENT_TAIL(blk_buf, fs->blocksize);
+		ext2fs_initialize_dirent_tail(fs, t);
+	}
+
+	retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
+	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 retval & BLOCK_ABORT ? 0 : retval;
+}
+
+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;
+
+	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;
+	retval = inline_data_search_dir2(fs, start, size, search_name, len);
+	if (!retval)
+		goto out;
+
+	retval = 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;
+	retval = inline_data_search_dir2(fs, start, size, search_name, len);
+
+out:
+	ext2fs_free_mem(&inode);
+	return retval;
+}
+
+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;
+	struct ext2_dir_entry_tail *t;
+	ext2_extent_handle_t handle;
+	errcode_t retval;
+	blk64_t blk;
+	void *inline_start;
+	char *backup_buf;
+	char *blk_buf;
+	int inline_size;
+	int i, limit, r;
+	int csum_size = 0;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+		csum_size = sizeof(struct ext2_dir_entry_tail);
+
+	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) {
+		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 */
+	retval = inline_data_destory_data(fs, ino, inode, &idata);
+	if (retval)
+		goto out;
+
+	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_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 - csum_size);
+
+	if (csum_size) {
+		t = EXT2_DIRENT_TAIL(blk_buf, fs->blocksize);
+		ext2fs_initialize_dirent_tail(fs, t);
+	}
+
+	retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
+	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 retval & BLOCK_ABORT ? 0 : retval;
+}