[automerger skipped] Merge changes from topic "discard" into oc-dev
am: d16862a25e  -s ours

Change-Id: I8a57695bc2963dfbb393c0ed1640e32935a7e233
diff --git a/.gitignore b/.gitignore
index 76473a6..debeffb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,3 +45,7 @@
 stamp-h1
 
 /mkfs/mkfs.f2fs
+/fsck/fsck.f2fs
+/tools/f2fstat
+/tools/fibmap.f2fs
+/tools/parse.f2fs
diff --git a/Android.mk b/Android.mk
index d0f4e76..afd719b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -4,7 +4,11 @@
 ifeq ($(HOST_OS),linux)
 
 # The versions depend on $(LOCAL_PATH)/VERSION
-version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=4 -DF2FS_TOOLS_VERSION=\"1.4.0\" -DF2FS_TOOLS_DATE=\"2014-10-18\" -DWITH_BLKDISCARD
+version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=8 -DF2FS_TOOLS_VERSION=\"1.8.0\" -DF2FS_TOOLS_DATE=\"2017-02-03\"
+common_CFLAGS := -DWITH_ANDROID $(version_CFLAGS)
+# Workaround for the <sys/types.h>/<sys/sysmacros.h> split, here now for
+# bionic and coming later for glibc.
+target_CFLAGS := $(common_CFLAGS) -include sys/sysmacros.h
 
 # external/e2fsprogs/lib is needed for uuid/uuid.h
 common_C_INCLUDES := $(LOCAL_PATH)/include external/e2fsprogs/lib/
@@ -18,7 +22,7 @@
 	mkfs/f2fs_format_utils.c \
 
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
 include $(BUILD_STATIC_LIBRARY)
 
@@ -31,7 +35,7 @@
 	mkfs/f2fs_format_utils.c \
 
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(common_CFLAGS)
 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
 include $(BUILD_HOST_STATIC_LIBRARY)
 
@@ -40,10 +44,12 @@
 LOCAL_MODULE := libf2fs_fmt_host_dyn
 LOCAL_SRC_FILES := \
 	lib/libf2fs.c \
+	lib/libf2fs_io.c \
 	mkfs/f2fs_format.c \
+	mkfs/f2fs_format_utils.c \
 
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(common_CFLAGS)
 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
 LOCAL_STATIC_LIBRARIES := \
      libf2fs_ioutils_host \
@@ -67,7 +73,7 @@
 	lib/libf2fs_io.c \
 	mkfs/f2fs_format_main.c
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
 LOCAL_STATIC_LIBRARIES := libc libf2fs_fmt libext2_uuid
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
@@ -80,7 +86,7 @@
 	lib/libf2fs_io.c \
 	mkfs/f2fs_format_main.c
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
 LOCAL_STATIC_LIBRARIES := libf2fs_fmt
 LOCAL_SHARED_LIBRARIES := libext2_uuid
 LOCAL_SYSTEM_SHARED_LIBRARIES := libc
@@ -100,7 +106,7 @@
 	lib/libf2fs_io.c \
 
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
 LOCAL_SHARED_LIBRARIES := libext2_uuid
 LOCAL_SYSTEM_SHARED_LIBRARIES := libc
 LOCAL_MODULE_TAGS := optional
@@ -118,7 +124,7 @@
 	lib/libf2fs_io.c \
 
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(common_CFLAGS)
 LOCAL_HOST_SHARED_LIBRARIES :=  libext2_uuid
 include $(BUILD_HOST_EXECUTABLE)
 
diff --git a/README b/README
index 222cbc3..4ea3356 100644
--- a/README
+++ b/README
@@ -7,24 +7,26 @@
 Before compilation
 ------------------
 
-Your should install the following packages.
+You should install the following packages.
  - libuuid-devel or uuid-dev
  - pkg-config
  - autoconf
  - libtool
+ - libselinux1-dev
 
 Initial compilation
 -------------------
 
 Before compilation initially, autoconf/automake tools should be run.
 
- # autoreconf --install
+ # ./autogen.sh
 
 How to compile
 --------------
 
  # ./configure
  # make
+ # make install
 
 How to cross-compile (e.g., for ARM)
 ------------------------------------
@@ -43,6 +45,6 @@
 How to run by default
 ---------------------
 
- $ ./mkfs.f2fs -l [LABEL] $DEV
+ $ mkfs.f2fs -l [LABEL] $DEV
 
 For more mkfs options, see man page.
diff --git a/VERSION b/VERSION
index 0e58c09..c520af0 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-1.4.1
-2015-03-04
+1.8.0
+2017-02-03
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..2b0945d
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+aclocal && \
+autoheader && \
+autoconf && \
+libtoolize && \
+automake -a -c
diff --git a/configure.ac b/configure.ac
index ae451b8..6a3f7c4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,6 +45,12 @@
 	sys/mount.h
 ])
 
+# Test configure options.
+AC_ARG_WITH([selinux],
+	AS_HELP_STRING([--without-selinux],
+	  [Ignore presence of libselinux and disable selinux support])
+)
+
 # Checks for programs.
 AC_PROG_CC
 AC_PROG_LIBTOOL
@@ -55,8 +61,21 @@
 # Checks for libraries.
 PKG_CHECK_MODULES([libuuid], [uuid])
 
+AS_IF([test "x$with_selinux" != "xno"],
+	[PKG_CHECK_MODULES([libselinux], [libselinux],
+	                   [have_selinux=yes], [have_selinux=no])],
+	[have_selinux=no]
+)
+
+AS_IF([test "x$have_selinux" = "xyes"],
+	[AC_DEFINE([HAVE_LIBSELINUX], [1], [Use libselinux])],
+	[AS_IF([test "x$with_selinux" = "xyes"],
+		[AC_MSG_ERROR([selinux support requested but libselinux not found])]
+	)]
+)
+
 # Checks for header files.
-AC_CHECK_HEADERS([linux/fs.h fcntl.h mntent.h stdlib.h string.h \
+AC_CHECK_HEADERS([linux/fs.h linux/blkzoned.h fcntl.h mntent.h stdlib.h string.h \
 		sys/ioctl.h sys/mount.h unistd.h linux/falloc.h byteswap.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
@@ -68,6 +87,7 @@
 # Checks for library functions.
 AC_FUNC_GETMNTENT
 AC_CHECK_FUNCS_ONCE([
+	fallocate
 	getmntent
 	memset
 ])
@@ -76,10 +96,10 @@
       [AC_CHECK_DECLS([bswap_64],,,[#include <byteswap.h>])])
 
 # Install directories
-AC_PREFIX_DEFAULT([/usr])
-AC_SUBST([sbindir], [/sbin])
-AC_SUBST([sysconfdir], [/etc])
-AC_SUBST([localstatedir], [/var])
+#AC_PREFIX_DEFAULT([/usr])
+#AC_SUBST([sbindir], [/sbin])
+#AC_SUBST([sysconfdir], [/etc])
+#AC_SUBST([localstatedir], [/var])
 AC_CONFIG_FILES([
 	Makefile
 	man/Makefile
@@ -89,4 +109,14 @@
 	tools/Makefile
 ])
 
+# export library version info for mkfs/libf2fs_format_la
+AC_SUBST(FMT_CURRENT, 1)
+AC_SUBST(FMT_REVISION, 0)
+AC_SUBST(FMT_AGE, 0)
+
+# export library version info for lib/libf2fs_la
+AC_SUBST(LIBF2FS_CURRENT, 2)
+AC_SUBST(LIBF2FS_REVISION, 0)
+AC_SUBST(LIBF2FS_AGE, 0)
+
 AC_OUTPUT
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 3258e47..7abcd00 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -3,8 +3,13 @@
 AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
 AM_CFLAGS = -Wall
 sbin_PROGRAMS = fsck.f2fs
-fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c
-fsck_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c f2fs.h fsck.h $(top_srcdir)/include/f2fs_fs.h	\
+		resize.c										\
+		node.c segment.c dir.c sload.c xattr.c
+fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
 
 install-data-hook:
 	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/dump.f2fs
+	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/defrag.f2fs
+	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
+	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/sload.f2fs
diff --git a/fsck/defrag.c b/fsck/defrag.c
new file mode 100644
index 0000000..bea0293
--- /dev/null
+++ b/fsck/defrag.c
@@ -0,0 +1,102 @@
+/**
+ * defrag.c
+ *
+ * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "fsck.h"
+
+static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to)
+{
+	void *raw = calloc(BLOCK_SZ, 1);
+	struct seg_entry *se;
+	struct f2fs_summary sum;
+	u64 offset;
+	int ret, type;
+
+	ASSERT(raw != NULL);
+
+	/* read from */
+	ret = dev_read_block(raw, from);
+	ASSERT(ret >= 0);
+
+	/* write to */
+	ret = dev_write_block(raw, to);
+	ASSERT(ret >= 0);
+
+	/* update sit bitmap & valid_blocks && se->type */
+	se = get_seg_entry(sbi, GET_SEGNO(sbi, from));
+	offset = OFFSET_IN_SEG(sbi, from);
+	type = se->type;
+	se->valid_blocks--;
+	f2fs_clear_bit(offset, (char *)se->cur_valid_map);
+	se->dirty = 1;
+
+	se = get_seg_entry(sbi, GET_SEGNO(sbi, to));
+	offset = OFFSET_IN_SEG(sbi, to);
+	se->type = type;
+	se->valid_blocks++;
+	f2fs_set_bit(offset, (char *)se->cur_valid_map);
+	se->dirty = 1;
+
+	/* read/write SSA */
+	get_sum_entry(sbi, from, &sum);
+	update_sum_entry(sbi, to, &sum);
+
+	/* if data block, read node and update node block */
+	if (IS_DATASEG(type))
+		update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
+				le16_to_cpu(sum.ofs_in_node), to);
+	else
+		update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to);
+
+	DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n",
+					IS_DATASEG(type) ? "data" : "node",
+					from, to);
+	free(raw);
+	return 0;
+}
+
+int f2fs_defragment(struct f2fs_sb_info *sbi, u64 from, u64 len, u64 to, int left)
+{
+	struct seg_entry *se;
+	u64 idx, offset;
+
+	/* flush NAT/SIT journal entries */
+	flush_journal_entries(sbi);
+
+	for (idx = from; idx < from + len; idx++) {
+		u64 target = to;
+
+		se = get_seg_entry(sbi, GET_SEGNO(sbi, idx));
+		offset = OFFSET_IN_SEG(sbi, idx);
+
+		if (!f2fs_test_bit(offset, (const char *)se->cur_valid_map))
+			continue;
+
+		if (find_next_free_block(sbi, &target, left, se->type)) {
+			MSG(0, "Not enough space to migrate blocks");
+			return -1;
+		}
+
+		if (migrate_block(sbi, idx, target)) {
+			ASSERT_MSG("Found inconsistency: please run FSCK");
+			return -1;
+		}
+	}
+
+	/* update curseg info; can update sit->types */
+	move_curseg_info(sbi, to);
+	zero_journal_entries(sbi);
+	write_curseg_info(sbi);
+
+	/* flush dirty sit entries */
+	flush_sit_entries(sbi);
+
+	write_checkpoint(sbi);
+
+	return 0;
+}
diff --git a/fsck/dir.c b/fsck/dir.c
new file mode 100644
index 0000000..d817d27
--- /dev/null
+++ b/fsck/dir.c
@@ -0,0 +1,597 @@
+/**
+ * dir.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "fsck.h"
+#include "node.h"
+
+static unsigned int dir_buckets(unsigned int level)
+{
+	if (level < MAX_DIR_HASH_DEPTH / 2)
+		return 1 << level;
+	else
+		return MAX_DIR_BUCKETS;
+}
+
+static unsigned int bucket_blocks(unsigned int level)
+{
+	if (level < MAX_DIR_HASH_DEPTH / 2)
+		return 2;
+	else
+		return 4;
+}
+
+static unsigned long dir_block_index(unsigned int level,
+		int dir_level, unsigned int idx)
+{
+	unsigned long i;
+	unsigned long bidx = 0;
+
+	for (i = 0; i < level; i++)
+		bidx += dir_buckets(i + dir_level) * bucket_blocks(i);
+	bidx += idx * bucket_blocks(level);
+	return bidx;
+}
+
+static int room_for_filename(const u8 *bitmap, int slots, int max_slots)
+{
+	int bit_start = 0;
+	int zero_start, zero_end;
+next:
+	zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start);
+	if (zero_start >= max_slots)
+		return max_slots;
+
+	zero_end = find_next_bit_le(bitmap, max_slots, zero_start + 1);
+
+	if (zero_end - zero_start >= slots)
+		return zero_start;
+	bit_start = zero_end;
+	goto next;
+
+}
+
+static void make_dentry_ptr(struct f2fs_dentry_ptr *d, void *src, int type)
+{
+	if (type == 1) {
+		struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src;
+		d->max = NR_DENTRY_IN_BLOCK;
+		d->bitmap = t->dentry_bitmap;
+		d->dentry = t->dentry;
+		d->filename = t->filename;
+	} else {
+		struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src;
+		d->max = NR_INLINE_DENTRY;
+		d->bitmap = t->dentry_bitmap;
+		d->dentry = t->dentry;
+		d->filename = t->filename;
+	}
+}
+
+static struct f2fs_dir_entry *find_target_dentry(const u8 *name,
+		unsigned int len, f2fs_hash_t namehash, int *max_slots,
+		struct f2fs_dentry_ptr *d)
+{
+	struct f2fs_dir_entry *de;
+	unsigned long bit_pos = 0;
+	int max_len = 0;
+
+	if (max_slots)
+		*max_slots = 0;
+	while (bit_pos < d->max) {
+		if (!test_bit_le(bit_pos, d->bitmap)) {
+			bit_pos++;
+			max_len++;
+			continue;
+		}
+
+		de = &d->dentry[bit_pos];
+		if (le16_to_cpu(de->name_len) == len &&
+			de->hash_code == namehash &&
+			!memcmp(d->filename[bit_pos], name, len)) {
+			goto found;
+		}
+
+		if (max_slots && max_len > *max_slots)
+			*max_slots = max_len;
+		max_len = 0;
+		bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
+	}
+	de = NULL;
+found:
+	if (max_slots && max_len > *max_slots)
+		*max_slots = max_len;
+	return de;
+}
+
+static struct f2fs_dir_entry *find_in_block(void *block,
+		const u8 *name, int len, f2fs_hash_t namehash,
+		int *max_slots)
+{
+	struct f2fs_dentry_ptr d;
+
+	make_dentry_ptr(&d, block, 1);
+	return find_target_dentry(name, len, namehash, max_slots, &d);
+}
+
+static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir,
+		unsigned int level, struct dentry *de)
+{
+	unsigned int nbucket, nblock;
+	unsigned int bidx, end_block;
+	struct f2fs_dir_entry *dentry = NULL;
+	struct dnode_of_data dn = {0};
+	void *dentry_blk;
+	int max_slots = 214;
+	nid_t ino = le32_to_cpu(dir->footer.ino);
+	f2fs_hash_t namehash;
+	int ret = 0;
+
+	namehash = f2fs_dentry_hash(de->name, de->len);
+
+	nbucket = dir_buckets(level);
+	nblock = bucket_blocks(level);
+
+	bidx = dir_block_index(level, 0, le32_to_cpu(namehash) % nbucket);
+	end_block = bidx + nblock;
+
+	dentry_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(dentry_blk);
+
+	for (; bidx < end_block; bidx++) {
+
+		/* Firstly, we should know direct node of target data blk */
+		if (dn.node_blk && dn.node_blk != dn.inode_blk)
+			free(dn.node_blk);
+
+		set_new_dnode(&dn, dir, NULL, ino);
+		get_dnode_of_data(sbi, &dn, bidx, LOOKUP_NODE);
+		if (dn.data_blkaddr == NULL_ADDR)
+			continue;
+
+		ret = dev_read_block(dentry_blk, dn.data_blkaddr);
+		ASSERT(ret >= 0);
+
+		dentry = find_in_block(dentry_blk, de->name, de->len,
+						namehash, &max_slots);
+		if (dentry) {
+			ret = 1;
+			de->ino = le32_to_cpu(dentry->ino);
+			break;
+		}
+	}
+
+	if (dn.node_blk && dn.node_blk != dn.inode_blk)
+		free(dn.node_blk);
+	free(dentry_blk);
+
+	return ret;
+}
+
+static int f2fs_find_entry(struct f2fs_sb_info *sbi,
+				struct f2fs_node *dir, struct dentry *de)
+{
+	unsigned int max_depth;
+	unsigned int level;
+
+	if (dir->i.i_inline & F2FS_INLINE_DENTRY) {
+		ERR_MSG("Not support to find \"%s\" in inline_dir pino=%x\n",
+				de->name, de->pino);
+		return 0;
+	}
+
+	max_depth = le32_to_cpu(dir->i.i_current_depth);
+	for (level = 0; level < max_depth; level ++) {
+		if (find_in_level(sbi, dir, level, de))
+			return 1;
+	}
+	return 0;
+}
+
+static void f2fs_update_dentry(nid_t ino, umode_t mode,
+		struct f2fs_dentry_ptr *d,
+		const unsigned char *name, int len, f2fs_hash_t name_hash,
+		unsigned int bit_pos)
+{
+	struct f2fs_dir_entry *de;
+	int slots = GET_DENTRY_SLOTS(len);
+	int i;
+
+	de = &d->dentry[bit_pos];
+	de->name_len = cpu_to_le16(len);
+	de->hash_code = name_hash;
+	memcpy(d->filename[bit_pos], name, len);
+	d->filename[bit_pos][len] = 0;
+	de->ino = cpu_to_le32(ino);
+	set_de_type(de, mode);
+	for (i = 0; i < slots; i++)
+		test_and_set_bit_le(bit_pos + i, d->bitmap);
+}
+
+/*
+ * f2fs_add_link - Add a new file(dir) to parent dir.
+ */
+static int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent,
+			struct f2fs_node *child, block_t p_blkaddr)
+{
+	int level = 0, current_depth, bit_pos;
+	int nbucket, nblock, bidx, block;
+	const unsigned char *name = child->i.i_name;
+	int name_len = le32_to_cpu(child->i.i_namelen);
+	int slots = GET_DENTRY_SLOTS(name_len);
+	f2fs_hash_t dentry_hash = f2fs_dentry_hash(name, name_len);
+	struct f2fs_dentry_block *dentry_blk;
+	struct f2fs_dentry_ptr d;
+	struct dnode_of_data dn = {0};
+	nid_t pino = le32_to_cpu(parent->footer.ino);
+	nid_t ino = le32_to_cpu(child->footer.ino);
+	umode_t mode = le16_to_cpu(child->i.i_mode);
+	int ret;
+
+	if (parent == NULL || child == NULL)
+		return -EINVAL;
+
+	if (!pino) {
+		ERR_MSG("Wrong parent ino:%d \n", pino);
+		return -EINVAL;
+	}
+
+	dentry_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(dentry_blk);
+
+	current_depth = le32_to_cpu(parent->i.i_current_depth);
+start:
+	if (current_depth == MAX_DIR_HASH_DEPTH) {
+		free(dentry_blk);
+		ERR_MSG("\tError: MAX_DIR_HASH\n");
+		return -ENOSPC;
+	}
+
+	/* Need a new dentry block */
+	if (level == current_depth)
+		++current_depth;
+
+	nbucket = dir_buckets(level);
+	nblock = bucket_blocks(level);
+	bidx = dir_block_index(level, 0, le32_to_cpu(dentry_hash) % nbucket);
+
+	for (block = bidx; block <= (bidx + nblock - 1); block++) {
+
+		/* Firstly, we should know the direct node of target data blk */
+		if (dn.node_blk && dn.node_blk != dn.inode_blk)
+			free(dn.node_blk);
+
+		set_new_dnode(&dn, parent, NULL, pino);
+		get_dnode_of_data(sbi, &dn, block, ALLOC_NODE);
+
+		if (dn.data_blkaddr == NULL_ADDR) {
+			new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA);
+		} else {
+			ret = dev_read_block(dentry_blk, dn.data_blkaddr);
+			ASSERT(ret >= 0);
+		}
+		bit_pos = room_for_filename(dentry_blk->dentry_bitmap,
+				slots, NR_DENTRY_IN_BLOCK);
+
+		if (bit_pos < NR_DENTRY_IN_BLOCK)
+			goto add_dentry;
+	}
+	level ++;
+	goto start;
+
+add_dentry:
+	make_dentry_ptr(&d, (void *)dentry_blk, 1);
+	f2fs_update_dentry(ino, mode, &d, name, name_len, dentry_hash, bit_pos);
+
+	ret = dev_write_block(dentry_blk, dn.data_blkaddr);
+	ASSERT(ret >= 0);
+
+	/*
+	 * Parent inode needs updating, because its inode info may be changed.
+	 * such as i_current_depth and i_blocks.
+	 */
+	if (parent->i.i_current_depth != cpu_to_le32(current_depth)) {
+		parent->i.i_current_depth = cpu_to_le32(current_depth);
+		dn.idirty = 1;
+	}
+
+	/* Update parent's i_links info*/
+	if (S_ISDIR(mode)) {
+		u32 links = le32_to_cpu(parent->i.i_links);
+		parent->i.i_links = cpu_to_le32(links + 1);
+		dn.idirty = 1;
+	}
+
+	if ((block + 1) * F2FS_BLKSIZE > le64_to_cpu(parent->i.i_size)) {
+		parent->i.i_size = cpu_to_le64((block + 1) * F2FS_BLKSIZE);
+		dn.idirty = 1;
+	}
+
+	if (dn.ndirty) {
+		ret = dev_write_block(dn.node_blk, dn.node_blkaddr);
+		ASSERT(ret >= 0);
+	}
+
+	if (dn.idirty) {
+		ASSERT(parent == dn.inode_blk);
+		ret = dev_write_block(dn.inode_blk, p_blkaddr);
+		ASSERT(ret >= 0);
+	}
+
+	if (dn.node_blk != dn.inode_blk)
+		free(dn.node_blk);
+	free(dentry_blk);
+	return 0;
+}
+
+static void make_empty_dir(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
+{
+	struct f2fs_dentry_block *dent_blk;
+	nid_t ino = le32_to_cpu(inode->footer.ino);
+	nid_t pino = le32_to_cpu(inode->i.i_pino);
+	struct f2fs_summary sum;
+	struct node_info ni;
+	block_t blkaddr;
+	int ret;
+
+	get_node_info(sbi, ino, &ni);
+
+	dent_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(dent_blk);
+
+	dent_blk->dentry[0].hash_code = 0;
+	dent_blk->dentry[0].ino = cpu_to_le32(ino);
+	dent_blk->dentry[0].name_len = cpu_to_le16(1);
+	dent_blk->dentry[0].file_type = F2FS_FT_DIR;
+	memcpy(dent_blk->filename[0], ".", 1);
+
+	dent_blk->dentry[1].hash_code = 0;
+	dent_blk->dentry[1].ino = cpu_to_le32(pino);
+	dent_blk->dentry[1].name_len = cpu_to_le16(2);
+	dent_blk->dentry[1].file_type = F2FS_FT_DIR;
+	memcpy(dent_blk->filename[1], "..", 2);
+
+	test_and_set_bit_le(0, dent_blk->dentry_bitmap);
+	test_and_set_bit_le(1, dent_blk->dentry_bitmap);
+
+	set_summary(&sum, ino, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_DATA);
+
+	ret = dev_write_block(dent_blk, blkaddr);
+	ASSERT(ret >= 0);
+
+	inode->i.i_addr[0] = cpu_to_le32(blkaddr);
+	free(dent_blk);
+}
+
+static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
+					const char *symname, int symlen)
+{
+	nid_t ino = le32_to_cpu(inode->footer.ino);
+	struct f2fs_summary sum;
+	struct node_info ni;
+	char *data_blk;
+	block_t blkaddr;
+	int ret;
+
+	get_node_info(sbi, ino, &ni);
+
+	/* store into inline_data */
+	if (symlen + 1 <= MAX_INLINE_DATA) {
+		inode->i.i_inline |= F2FS_INLINE_DATA;
+		inode->i.i_inline |= F2FS_DATA_EXIST;
+		memcpy(&inode->i.i_addr[1], symname, symlen);
+		return;
+	}
+
+	data_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(data_blk);
+
+	memcpy(data_blk, symname, symlen);
+
+	set_summary(&sum, ino, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA);
+
+	ret = dev_write_block(data_blk, blkaddr);
+	ASSERT(ret >= 0);
+
+	inode->i.i_addr[0] = cpu_to_le32(blkaddr);
+	free(data_blk);
+}
+
+static void init_inode_block(struct f2fs_sb_info *sbi,
+		struct f2fs_node *node_blk, struct dentry *de)
+{
+	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	mode_t mode = de->mode;
+	int links = 1;
+	unsigned int size;
+	int blocks = 1;
+
+	if (de->file_type == F2FS_FT_DIR) {
+		mode |= S_IFDIR;
+		size = 4096;
+		links++;
+		blocks++;
+	} else if (de->file_type == F2FS_FT_REG_FILE) {
+		mode |= S_IFREG;
+		size = 0;
+	} else if (de->file_type == F2FS_FT_SYMLINK) {
+		ASSERT(de->link);
+		mode |= S_IFLNK;
+		size = strlen(de->link);
+		if (size + 1 > MAX_INLINE_DATA)
+			blocks++;
+	} else {
+		ASSERT(0);
+	}
+
+	node_blk->i.i_mode = cpu_to_le16(mode);
+	node_blk->i.i_advise = 0;
+	node_blk->i.i_uid = cpu_to_le32(de->uid);
+	node_blk->i.i_gid = cpu_to_le32(de->gid);
+	node_blk->i.i_links = cpu_to_le32(links);
+	node_blk->i.i_size = cpu_to_le32(size);
+	node_blk->i.i_blocks = cpu_to_le32(blocks);
+	node_blk->i.i_atime = cpu_to_le64(de->mtime);
+	node_blk->i.i_ctime = cpu_to_le64(de->mtime);
+	node_blk->i.i_mtime = cpu_to_le64(de->mtime);
+	node_blk->i.i_atime_nsec = 0;
+	node_blk->i.i_ctime_nsec = 0;
+	node_blk->i.i_mtime_nsec = 0;
+	node_blk->i.i_generation = 0;
+	node_blk->i.i_current_depth = cpu_to_le32(1);
+	node_blk->i.i_xattr_nid = 0;
+	node_blk->i.i_flags = 0;
+	node_blk->i.i_inline = F2FS_INLINE_XATTR;
+	node_blk->i.i_pino = cpu_to_le32(de->pino);
+	node_blk->i.i_namelen = cpu_to_le32(de->len);
+	memcpy(node_blk->i.i_name, de->name, de->len);
+	node_blk->i.i_name[de->len] = 0;
+
+	node_blk->footer.ino = cpu_to_le32(de->ino);
+	node_blk->footer.nid = cpu_to_le32(de->ino);
+	node_blk->footer.flag = 0;
+	node_blk->footer.cp_ver = ckpt->checkpoint_ver;
+
+	if (S_ISDIR(mode))
+		make_empty_dir(sbi, node_blk);
+	else if (S_ISLNK(mode))
+		page_symlink(sbi, node_blk, de->link, size);
+}
+
+int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	struct f2fs_node *parent, *child;
+	struct node_info ni;
+	struct f2fs_summary sum;
+	block_t blkaddr;
+	int ret;
+
+	/* Find if there is a */
+	get_node_info(sbi, de->pino, &ni);
+	if (ni.blk_addr == NULL_ADDR) {
+		MSG(0, "No parent directory pino=%x\n", de->pino);
+		return -1;
+	}
+
+	parent = calloc(BLOCK_SZ, 1);
+	ASSERT(parent);
+
+	ret = dev_read_block(parent, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	ret = f2fs_find_entry(sbi, parent, de);
+	if (ret) {
+		MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
+					de->name, de->pino, ret);
+		if (de->file_type == F2FS_FT_REG_FILE)
+			de->ino = 0;
+		goto free_parent_dir;
+	}
+	if (parent->i.i_inline & F2FS_INLINE_DENTRY) {
+		ERR_MSG("Not support adding \"%s\" in inline_dir pino=%x\n",
+					de->name, de->pino);
+		if (de->file_type == F2FS_FT_REG_FILE)
+			de->ino = 0;
+		goto free_parent_dir;
+	}
+
+	child = calloc(BLOCK_SZ, 1);
+	ASSERT(child);
+
+	f2fs_alloc_nid(sbi, &de->ino, 1);
+
+	init_inode_block(sbi, child, de);
+
+	ret = f2fs_add_link(sbi, parent, child, ni.blk_addr);
+	if (ret) {
+		MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
+					de->name, de->pino, ret);
+		goto free_child_dir;
+	}
+
+	/* write child */
+	set_summary(&sum, de->ino, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE);
+
+	/* update nat info */
+	update_nat_blkaddr(sbi, de->ino, de->ino, blkaddr);
+
+	ret = dev_write_block(child, blkaddr);
+	ASSERT(ret >= 0);
+
+	update_free_segments(sbi);
+	MSG(1, "Info: Create \"%s\" type=%x, ino=%x / %x into \"%s\"\n",
+			de->full_path, de->file_type,
+			de->ino, de->pino, de->path);
+free_child_dir:
+	free(child);
+free_parent_dir:
+	free(parent);
+	return 0;
+}
+
+int f2fs_mkdir(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	return f2fs_create(sbi, de);
+}
+
+int f2fs_symlink(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	return f2fs_create(sbi, de);
+}
+
+int f2fs_find_path(struct f2fs_sb_info *sbi, char *path, nid_t *ino)
+{
+	struct f2fs_node *parent;
+	struct node_info ni;
+	struct dentry de;
+	int err = 0;
+	int ret;
+	char *p;
+
+	if (path[0] != '/')
+		return -ENOENT;
+
+	*ino = F2FS_ROOT_INO(sbi);
+	parent = calloc(BLOCK_SZ, 1);
+	ASSERT(parent);
+
+	p = strtok(path, "/");
+	while (p) {
+		de.name = (const u8 *)p;
+		de.len = strlen(p);
+
+		get_node_info(sbi, *ino, &ni);
+		if (ni.blk_addr == NULL_ADDR) {
+			err = -ENOENT;
+			goto err;
+		}
+		ret = dev_read_block(parent, ni.blk_addr);
+		ASSERT(ret >= 0);
+
+		ret = f2fs_find_entry(sbi, parent, &de);
+		if (!ret) {
+			err = -ENOENT;
+			goto err;
+		}
+
+		*ino = de.ino;
+		p = strtok(NULL, "/");
+	}
+err:
+	free(parent);
+	return err;
+}
diff --git a/fsck/dump.c b/fsck/dump.c
index 3c4a8d1..8cbeda1 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -15,57 +15,177 @@
 
 #define BUF_SZ	80
 
-const char *seg_type_name[SEG_TYPE_MAX] = {
+const char *seg_type_name[SEG_TYPE_MAX + 1] = {
 	"SEG_TYPE_DATA",
 	"SEG_TYPE_CUR_DATA",
 	"SEG_TYPE_NODE",
 	"SEG_TYPE_CUR_NODE",
+	"SEG_TYPE_NONE",
 };
 
-void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit)
+void nat_dump(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	struct f2fs_nat_block *nat_block;
+	struct f2fs_node *node_block;
+	u32 nr_nat_blks, nid;
+	pgoff_t block_off;
+	pgoff_t block_addr;
+	char buf[BUF_SZ];
+	int seg_off;
+	int fd, ret, pack;
+	unsigned int i;
+
+	nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+	node_block = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+	ASSERT(nat_block);
+
+	nr_nat_blks = get_sb(segment_count_nat) <<
+				(sbi->log_blocks_per_seg - 1);
+
+	fd = open("dump_nat", O_CREAT|O_WRONLY|O_TRUNC, 0666);
+	ASSERT(fd >= 0);
+
+	for (block_off = 0; block_off < nr_nat_blks; pack = 1, block_off++) {
+
+		seg_off = block_off >> sbi->log_blocks_per_seg;
+		block_addr = (pgoff_t)(nm_i->nat_blkaddr +
+			(seg_off << sbi->log_blocks_per_seg << 1) +
+			(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+
+		if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) {
+			block_addr += sbi->blocks_per_seg;
+			pack = 2;
+		}
+
+		ret = dev_read_block(nat_block, block_addr);
+		ASSERT(ret >= 0);
+
+		nid = block_off * NAT_ENTRY_PER_BLOCK;
+		for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) {
+			struct f2fs_nat_entry raw_nat;
+			struct node_info ni;
+			ni.nid = nid + i;
+
+			if(nid + i  == 0 || nid + i  == 1 || nid + i == 2 )
+				continue;
+			if (lookup_nat_in_journal(sbi, nid + i,
+							&raw_nat) >= 0) {
+				node_info_from_raw_nat(&ni, &raw_nat);
+				ret = dev_read_block(node_block, ni.blk_addr);
+				ASSERT(ret >= 0);
+				if (ni.blk_addr != 0x0) {
+					memset(buf, 0, BUF_SZ);
+					snprintf(buf, BUF_SZ,
+						"nid:%5u\tino:%5u\toffset:%5u"
+						"\tblkaddr:%10u\tpack:%d\n",
+						ni.nid, ni.ino,
+						le32_to_cpu(node_block->footer.flag) >>
+							OFFSET_BIT_SHIFT,
+						ni.blk_addr, pack);
+					ret = write(fd, buf, strlen(buf));
+					ASSERT(ret >= 0);
+				}
+			} else {
+				node_info_from_raw_nat(&ni,
+						&nat_block->entries[i]);
+				if (ni.blk_addr == 0)
+					continue;
+
+				ret = dev_read_block(node_block, ni.blk_addr);
+				ASSERT(ret >= 0);
+				memset(buf, 0, BUF_SZ);
+				snprintf(buf, BUF_SZ,
+					"nid:%5u\tino:%5u\toffset:%5u"
+					"\tblkaddr:%10u\tpack:%d\n",
+					ni.nid, ni.ino,
+					le32_to_cpu(node_block->footer.flag) >>
+						OFFSET_BIT_SHIFT,
+					ni.blk_addr, pack);
+				ret = write(fd, buf, strlen(buf));
+				ASSERT(ret >= 0);
+			}
+		}
+	}
+
+	free(nat_block);
+	free(node_block);
+
+	close(fd);
+}
+
+void sit_dump(struct f2fs_sb_info *sbi, unsigned int start_sit,
+					unsigned int end_sit)
 {
 	struct seg_entry *se;
-	int segno;
+	struct sit_info *sit_i = SIT_I(sbi);
+	unsigned int segno;
 	char buf[BUF_SZ];
 	u32 free_segs = 0;;
 	u64 valid_blocks = 0;
 	int ret;
-	int fd;
+	int fd, i;
+	unsigned int offset;
 
 	fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666);
 	ASSERT(fd >= 0);
 
+	snprintf(buf, BUF_SZ, "segment_type(0:HD, 1:WD, 2:CD, "
+						"3:HN, 4:WN, 5:CN)\n");
+	ret = write(fd, buf, strlen(buf));
+	ASSERT(ret >= 0);
+
 	for (segno = start_sit; segno < end_sit; segno++) {
 		se = get_seg_entry(sbi, segno);
-
+		offset = SIT_BLOCK_OFFSET(sit_i, segno);
 		memset(buf, 0, BUF_SZ);
-		snprintf(buf, BUF_SZ, "%5d %8d\n", segno, se->valid_blocks);
+		snprintf(buf, BUF_SZ,
+		"\nsegno:%8u\tvblocks:%3u\tseg_type:%d\tsit_pack:%d\n\n",
+			segno, se->valid_blocks, se->type,
+			f2fs_test_bit(offset, sit_i->sit_bitmap) ? 2 : 1);
 
 		ret = write(fd, buf, strlen(buf));
 		ASSERT(ret >= 0);
 
-		DBG(4, "SIT[0x%3x] : 0x%x\n", segno, se->valid_blocks);
 		if (se->valid_blocks == 0x0) {
 			free_segs++;
-		} else {
-			ASSERT(se->valid_blocks <= 512);
-			valid_blocks += se->valid_blocks;
+			continue;
+		}
+
+		ASSERT(se->valid_blocks <= 512);
+		valid_blocks += se->valid_blocks;
+
+		for (i = 0; i < 64; i++) {
+			memset(buf, 0, BUF_SZ);
+			snprintf(buf, BUF_SZ, "  %02x",
+					*(se->cur_valid_map + i));
+			ret = write(fd, buf, strlen(buf));
+			ASSERT(ret >= 0);
+
+			if ((i + 1) % 16 == 0) {
+				snprintf(buf, BUF_SZ, "\n");
+				ret = write(fd, buf, strlen(buf));
+				ASSERT(ret >= 0);
+			}
 		}
 	}
 
 	memset(buf, 0, BUF_SZ);
-	snprintf(buf, BUF_SZ, "valid_segs:%d\t free_segs:%d\n",
-			SM_I(sbi)->main_segments - free_segs, free_segs);
+	snprintf(buf, BUF_SZ,
+		"valid_blocks:[0x%" PRIx64 "]\tvalid_segs:%d\t free_segs:%d\n",
+			valid_blocks,
+			SM_I(sbi)->main_segments - free_segs,
+			free_segs);
 	ret = write(fd, buf, strlen(buf));
 	ASSERT(ret >= 0);
 
 	close(fd);
-	DBG(1, "Blocks [0x%" PRIx64 "] Free Segs [0x%x]\n", valid_blocks, free_segs);
 }
 
 void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa)
 {
-	struct f2fs_summary_block sum_blk;
+	struct f2fs_summary_block *sum_blk;
 	char buf[BUF_SZ];
 	int segno, i, ret;
 	int fd;
@@ -80,7 +200,7 @@
 	ASSERT(ret >= 0);
 
 	for (segno = start_ssa; segno < end_ssa; segno++) {
-		ret = get_sum_block(sbi, segno, &sum_blk);
+		sum_blk = get_sum_block(sbi, segno, &ret);
 
 		memset(buf, 0, BUF_SZ);
 		switch (ret) {
@@ -108,15 +228,18 @@
 				ASSERT(ret >= 0);
 			}
 			snprintf(buf, BUF_SZ, "[%3d: %6x]", i,
-					le32_to_cpu(sum_blk.entries[i].nid));
+					le32_to_cpu(sum_blk->entries[i].nid));
 			ret = write(fd, buf, strlen(buf));
 			ASSERT(ret >= 0);
 		}
+		if (ret == SEG_TYPE_NODE || ret == SEG_TYPE_DATA ||
+					ret == SEG_TYPE_MAX)
+			free(sum_blk);
 	}
 	close(fd);
 }
 
-static void dump_data_blk(__u64 offset, u32 blkaddr)
+static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
 {
 	char buf[F2FS_BLKSIZE];
 
@@ -124,7 +247,7 @@
 		return;
 
 	/* get data */
-	if (blkaddr == NEW_ADDR) {
+	if (blkaddr == NEW_ADDR || !IS_VALID_BLK_ADDR(sbi, blkaddr)) {
 		memset(buf, 0, F2FS_BLKSIZE);
 	} else {
 		int ret;
@@ -171,7 +294,7 @@
 	for (i = 0; i < idx; i++, (*ofs)++) {
 		switch (ntype) {
 		case TYPE_DIRECT_NODE:
-			dump_data_blk(*ofs * F2FS_BLKSIZE,
+			dump_data_blk(sbi, *ofs * F2FS_BLKSIZE,
 					le32_to_cpu(node_blk->dn.addr[i]));
 			break;
 		case TYPE_INDIRECT_NODE:
@@ -205,69 +328,74 @@
 
 	/* check data blocks in inode */
 	for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
-		dump_data_blk(ofs * F2FS_BLKSIZE,
+		dump_data_blk(sbi, ofs * F2FS_BLKSIZE,
 				le32_to_cpu(node_blk->i.i_addr[i]));
 
 	/* check node blocks in inode */
 	for (i = 0; i < 5; i++) {
 		if (i == 0 || i == 1)
 			dump_node_blk(sbi, TYPE_DIRECT_NODE,
-					node_blk->i.i_nid[i], &ofs);
+					le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
 		else if (i == 2 || i == 3)
 			dump_node_blk(sbi, TYPE_INDIRECT_NODE,
-					node_blk->i.i_nid[i], &ofs);
+					le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
 		else if (i == 4)
 			dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE,
-					node_blk->i.i_nid[i], &ofs);
+					le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
 		else
 			ASSERT(0);
 	}
 }
 
-void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
-					struct f2fs_node *node_blk)
+static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
+				struct f2fs_node *node_blk, int force)
 {
 	struct f2fs_inode *inode = &node_blk->i;
 	u32 imode = le32_to_cpu(inode->i_mode);
-	char name[255] = {0};
+	u32 namelen = le32_to_cpu(inode->i_namelen);
+	unsigned char name[F2FS_NAME_LEN + 1] = {0};
 	char path[1024] = {0};
 	char ans[255] = {0};
+	int is_encrypt = file_is_encrypt(inode);
 	int ret;
 
-	if (!S_ISREG(imode)) {
-		MSG(0, "Not a regular file\n\n");
+	if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
+		MSG(force, "Not a regular file or wrong name info\n\n");
 		return;
 	}
+	if (force)
+		goto dump;
 
 	printf("Do you want to dump this file into ./lost_found/? [Y/N] ");
 	ret = scanf("%s", ans);
 	ASSERT(ret >= 0);
 
 	if (!strcasecmp(ans, "y")) {
+dump:
 		ret = system("mkdir -p ./lost_found");
 		ASSERT(ret >= 0);
 
 		/* make a file */
-		strncpy(name, (const char *)inode->i_name,
-					le32_to_cpu(inode->i_namelen));
-		name[le32_to_cpu(inode->i_namelen)] = 0;
+		namelen = convert_encrypted_name(inode->i_name, namelen,
+							name, is_encrypt);
+		name[namelen] = 0;
 		sprintf(path, "./lost_found/%s", name);
 
-		config.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
-		ASSERT(config.dump_fd >= 0);
+		c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
+		ASSERT(c.dump_fd >= 0);
 
 		/* dump file's data */
 		dump_inode_blk(sbi, ni->ino, node_blk);
 
 		/* adjust file size */
-		ret = ftruncate(config.dump_fd, le32_to_cpu(inode->i_size));
+		ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size));
 		ASSERT(ret >= 0);
 
-		close(config.dump_fd);
+		close(c.dump_fd);
 	}
 }
 
-void dump_node(struct f2fs_sb_info *sbi, nid_t nid)
+void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
 {
 	struct node_info ni;
 	struct f2fs_node *node_blk;
@@ -283,17 +411,19 @@
 	DBG(1, "nat_entry.ino         [0x%x]\n", ni.ino);
 
 	if (ni.blk_addr == 0x0)
-		MSG(0, "Invalid nat entry\n\n");
+		MSG(force, "Invalid nat entry\n\n");
 
 	DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino));
 	DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid));
 
 	if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
-			le32_to_cpu(node_blk->footer.nid) == ni.nid) {
-		print_node_info(node_blk);
-		dump_file(sbi, &ni, node_blk);
+			le32_to_cpu(node_blk->footer.nid) == ni.nid &&
+			ni.ino == ni.nid) {
+		print_node_info(node_blk, force);
+		dump_file(sbi, &ni, node_blk, force);
 	} else {
-		MSG(0, "Invalid node block\n\n");
+		print_node_info(node_blk, force);
+		MSG(force, "Invalid (i)node block\n\n");
 	}
 
 	free(node_blk);
@@ -310,8 +440,8 @@
 	ret = dev_read_block(node_blk, blk_addr);
 	ASSERT(ret >= 0);
 
-	if (config.dbg_lv > 0)
-		print_node_info(node_blk);
+	if (c.dbg_lv > 0)
+		print_node_info(node_blk, 0);
 	else
 		print_inode_info(&node_blk->i, 1);
 
@@ -418,7 +548,9 @@
 	DBG(1, " - Segno              [0x%x]\n", GET_SEGNO(sbi, blk_addr));
 	DBG(1, " - Offset             [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr));
 	DBG(1, "SUM.nid               [0x%x]\n", nid);
-	DBG(1, "SUM.type              [%s]\n", seg_type_name[type]);
+	DBG(1, "SUM.type              [%s]\n", type >= 0 ?
+						seg_type_name[type] :
+						"Broken");
 	DBG(1, "SUM.version           [%d]\n", sum_entry.version);
 	DBG(1, "SUM.ofs_in_node       [0x%x]\n", sum_entry.ofs_in_node);
 	DBG(1, "NAT.blkaddr           [0x%x]\n", ni.blk_addr);
@@ -434,7 +566,7 @@
 	}
 
 	/* print inode */
-	if (config.dbg_lv > 0)
+	if (c.dbg_lv > 0)
 		dump_node_from_blkaddr(ino_ni.blk_addr);
 
 	if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index c268f15..5c8eea5 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -19,8 +19,6 @@
 #include <string.h>
 #include <errno.h>
 #include <mntent.h>
-#include <linux/types.h>
-#include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -60,6 +58,7 @@
 
 	char *nat_bitmap;
 	int bitmap_size;
+	char *nid_bitmap;
 };
 
 struct seg_entry {
@@ -74,6 +73,7 @@
 	unsigned char type;             /* segment type like CURSEG_XXX_TYPE */
 	unsigned char orig_type;        /* segment type like CURSEG_XXX_TYPE */
 	unsigned long long mtime;       /* modification time of the segment */
+	int dirty;
 };
 
 struct sec_entry {
@@ -123,6 +123,44 @@
 	unsigned int ovp_segments;
 };
 
+struct f2fs_dentry_ptr {
+	struct inode *inode;
+	u8 *bitmap;
+	struct f2fs_dir_entry *dentry;
+	__u8 (*filename)[F2FS_SLOT_LEN];
+	int max;
+};
+
+struct dentry {
+	char *path;
+	char *full_path;
+	const u8 *name;
+	int len;
+	char *link;
+	unsigned long size;
+	u8 file_type;
+	u16 mode;
+	u16 uid;
+	u16 gid;
+	u32 *inode;
+	u32 mtime;
+	char *secon;
+	uint64_t capabilities;
+	nid_t ino;
+	nid_t pino;
+};
+
+/* different from dnode_of_data in kernel */
+struct dnode_of_data {
+	struct f2fs_node *inode_blk;	/* inode page */
+	struct f2fs_node *node_blk;	/* cached direct node page */
+	nid_t nid;
+	unsigned int ofs_in_node;
+	block_t data_blkaddr;
+	block_t node_blkaddr;
+	int idirty, ndirty;
+};
+
 struct f2fs_sb_info {
 	struct f2fs_fsck *fsck;
 
@@ -159,7 +197,7 @@
 	u32 s_next_generation;                  /* for NFS support */
 
 	unsigned int cur_victim_sec;            /* current victim section num */
-
+	u32 free_segments;
 };
 
 static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
@@ -240,19 +278,10 @@
 
 static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi)
 {
-	block_t start_addr;
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
-	unsigned long long ckpt_version = le64_to_cpu(ckpt->checkpoint_ver);
+	block_t start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
 
-	start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
-
-	/*
-	 * odd numbered checkpoint should at cp segment 0
-	 * and even segent must be at cp segment 1
-	 */
-	if (!(ckpt_version & 1))
+	if (sbi->cur_cp == 2)
 		start_addr += sbi->blocks_per_seg;
-
 	return start_addr;
 }
 
@@ -295,7 +324,7 @@
 #define GET_R2L_SEGNO(sbi, segno)	(segno + FREE_I_START_SEGNO(sbi))
 
 #define START_BLOCK(sbi, segno)	(SM_I(sbi)->main_blkaddr +		\
-	(segno << sbi->log_blocks_per_seg))
+	((segno) << sbi->log_blocks_per_seg))
 
 static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type)
 {
@@ -313,25 +342,24 @@
 		- (base + 1) + type;
 }
 
+#define nats_in_cursum(jnl)             (le16_to_cpu(jnl->n_nats))
+#define sits_in_cursum(jnl)             (le16_to_cpu(jnl->n_sits))
 
-#define nats_in_cursum(sum)             (le16_to_cpu(sum->n_nats))
-#define sits_in_cursum(sum)             (le16_to_cpu(sum->n_sits))
-
-#define nat_in_journal(sum, i)          (sum->nat_j.entries[i].ne)
-#define nid_in_journal(sum, i)          (sum->nat_j.entries[i].nid)
-#define sit_in_journal(sum, i)          (sum->sit_j.entries[i].se)
-#define segno_in_journal(sum, i)        (sum->sit_j.entries[i].segno)
+#define nat_in_journal(jnl, i)          (jnl->nat_j.entries[i].ne)
+#define nid_in_journal(jnl, i)          (jnl->nat_j.entries[i].nid)
+#define sit_in_journal(jnl, i)          (jnl->sit_j.entries[i].se)
+#define segno_in_journal(jnl, i)        (jnl->sit_j.entries[i].segno)
 
 #define SIT_ENTRY_OFFSET(sit_i, segno)                                  \
-	(segno % sit_i->sents_per_block)
+	((segno) % sit_i->sents_per_block)
 #define SIT_BLOCK_OFFSET(sit_i, segno)                                  \
-	(segno / SIT_ENTRY_PER_BLOCK)
+	((segno) / SIT_ENTRY_PER_BLOCK)
 #define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
 
 static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid)
 {
 	return (nid <= (NAT_ENTRY_PER_BLOCK *
-			F2FS_RAW_SUPER(sbi)->segment_count_nat
+			le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment_count_nat)
 			<< (sbi->log_blocks_per_seg - 1)));
 }
 
@@ -339,9 +367,9 @@
 {
 	int i;
 
-	if (addr >= F2FS_RAW_SUPER(sbi)->block_count ||
+	if (addr >= le64_to_cpu(F2FS_RAW_SUPER(sbi)->block_count) ||
 				addr < SM_I(sbi)->main_blkaddr) {
-		ASSERT_MSG("block addr [0x%x]\n", addr);
+		DBG(1, "block addr [0x%x]\n", addr);
 		return 0;
 	}
 
@@ -355,6 +383,22 @@
 	return 1;
 }
 
+static inline int IS_CUR_SEGNO(struct f2fs_sb_info *sbi, u32 segno, int type)
+{
+	int i;
+
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+
+		if (type == i)
+			continue;
+
+		if (segno == curseg->segno)
+			return 1;
+	}
+	return 0;
+}
+
 static inline u64 BLKOFF_FROM_MAIN(struct f2fs_sb_info *sbi, u64 blk_addr)
 {
 	ASSERT(blk_addr >= SM_I(sbi)->main_blkaddr);
@@ -381,7 +425,44 @@
 	ni->version = raw_nat->version;
 }
 
+static inline void set_summary(struct f2fs_summary *sum, nid_t nid,
+			unsigned int ofs_in_node, unsigned char version)
+{
+	sum->nid = cpu_to_le32(nid);
+	sum->ofs_in_node = cpu_to_le16(ofs_in_node);
+	sum->version = version;
+}
+
+#define S_SHIFT 12
+static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = {
+	[S_IFREG >> S_SHIFT]    = F2FS_FT_REG_FILE,
+	[S_IFDIR >> S_SHIFT]    = F2FS_FT_DIR,
+	[S_IFCHR >> S_SHIFT]    = F2FS_FT_CHRDEV,
+	[S_IFBLK >> S_SHIFT]    = F2FS_FT_BLKDEV,
+	[S_IFIFO >> S_SHIFT]    = F2FS_FT_FIFO,
+	[S_IFSOCK >> S_SHIFT]   = F2FS_FT_SOCK,
+	[S_IFLNK >> S_SHIFT]    = F2FS_FT_SYMLINK,
+};
+
+static inline void set_de_type(struct f2fs_dir_entry *de, umode_t mode)
+{
+	de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
+}
+
+static inline void *inline_xattr_addr(struct f2fs_inode *inode)
+{
+	return (void *)&(inode->i_addr[DEF_ADDRS_PER_INODE_INLINE_XATTR]);
+}
+
+static inline int inline_xattr_size(struct f2fs_inode *inode)
+{
+	if (inode->i_inline & F2FS_INLINE_XATTR)
+		return F2FS_INLINE_XATTR_ADDRS << 2;
+	return 0;
+}
+
 extern int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *ne);
 #define IS_SUM_NODE_SEG(footer)		(footer.entry_type == SUM_TYPE_NODE)
+#define IS_SUM_DATA_SEG(footer)		(footer.entry_type == SUM_TYPE_DATA)
 
 #endif /* _F2FS_H_ */
diff --git a/fsck/fsck.c b/fsck/fsck.c
index 1b27ae0..e97ee0a 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -18,21 +18,19 @@
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	struct seg_entry *se;
+	int fix = 0;
 
 	se = get_seg_entry(sbi, GET_SEGNO(sbi, blk));
-	if (se->type != type) {
-		if (type == CURSEG_WARM_DATA) {
-			if (se->type != CURSEG_COLD_DATA) {
-				DBG(1, "Wrong segment type [0x%x] %x -> %x",
-						GET_SEGNO(sbi, blk), se->type,
-						CURSEG_WARM_DATA);
-				se->type = CURSEG_WARM_DATA;
-			}
-		} else {
-			DBG(1, "Wrong segment type [0x%x] %x -> %x",
+	if (se->type >= NO_CHECK_TYPE)
+		fix = 1;
+	else if (IS_DATASEG(se->type) != IS_DATASEG(type))
+		fix = 1;
+
+	/* just check data and node types */
+	if (fix) {
+		DBG(1, "Wrong segment type [0x%x] %x -> %x",
 				GET_SEGNO(sbi, blk), se->type, type);
-			se->type = type;
-		}
+		se->type = type;
 	}
 	return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->main_area_bitmap);
 }
@@ -63,6 +61,7 @@
 
 	node->nid = nid;
 	node->links = link_cnt;
+	node->actual_links = 1;
 	node->next = NULL;
 
 	if (fsck->hard_link_list_head == NULL) {
@@ -112,6 +111,7 @@
 
 	/* Decrease link count */
 	node->links = node->links - 1;
+	node->actual_links++;
 
 	/* if link count becomes one, remove the node */
 	if (node->links == 1) {
@@ -127,72 +127,227 @@
 static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid,
 							u32 blk_addr)
 {
+	struct f2fs_summary_block *sum_blk;
+	struct f2fs_summary *sum_entry;
+	struct seg_entry * se;
+	u32 segno, offset;
+	int need_fix = 0, ret = 0;
+	int type;
+
+	segno = GET_SEGNO(sbi, blk_addr);
+	offset = OFFSET_IN_SEG(sbi, blk_addr);
+
+	sum_blk = get_sum_block(sbi, segno, &type);
+
+	if (type != SEG_TYPE_NODE && type != SEG_TYPE_CUR_NODE) {
+		/* can't fix current summary, then drop the block */
+		if (!c.fix_on || type < 0) {
+			ASSERT_MSG("Summary footer is not for node segment");
+			ret = -EINVAL;
+			goto out;
+		}
+
+		need_fix = 1;
+		se = get_seg_entry(sbi, segno);
+		if(IS_NODESEG(se->type)) {
+			FIX_MSG("Summary footer indicates a node segment: 0x%x", segno);
+			sum_blk->footer.entry_type = SUM_TYPE_NODE;
+		} else {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	sum_entry = &(sum_blk->entries[offset]);
+
+	if (le32_to_cpu(sum_entry->nid) != nid) {
+		if (!c.fix_on || type < 0) {
+			DBG(0, "nid                       [0x%x]\n", nid);
+			DBG(0, "target blk_addr           [0x%x]\n", blk_addr);
+			DBG(0, "summary blk_addr          [0x%x]\n",
+						GET_SUM_BLKADDR(sbi,
+						GET_SEGNO(sbi, blk_addr)));
+			DBG(0, "seg no / offset           [0x%x / 0x%x]\n",
+						GET_SEGNO(sbi, blk_addr),
+						OFFSET_IN_SEG(sbi, blk_addr));
+			DBG(0, "summary_entry.nid         [0x%x]\n",
+						le32_to_cpu(sum_entry->nid));
+			DBG(0, "--> node block's nid      [0x%x]\n", nid);
+			ASSERT_MSG("Invalid node seg summary\n");
+			ret = -EINVAL;
+		} else {
+			FIX_MSG("Set node summary 0x%x -> [0x%x] [0x%x]",
+						segno, nid, blk_addr);
+			sum_entry->nid = cpu_to_le32(nid);
+			need_fix = 1;
+		}
+	}
+	if (need_fix && !c.ro) {
+		u64 ssa_blk;
+		int ret2;
+
+		ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+		ret2 = dev_write_block(sum_blk, ssa_blk);
+		ASSERT(ret2 >= 0);
+	}
+out:
+	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+					type == SEG_TYPE_MAX)
+		free(sum_blk);
+	return ret;
+}
+
+static int is_valid_summary(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
+							u32 blk_addr)
+{
+	u16 ofs_in_node = le16_to_cpu(sum->ofs_in_node);
+	u32 nid = le32_to_cpu(sum->nid);
+	struct f2fs_node *node_blk = NULL;
+	__le32 target_blk_addr;
+	struct node_info ni;
 	int ret = 0;
-	struct f2fs_summary sum_entry;
 
-	ret = get_sum_entry(sbi, blk_addr, &sum_entry);
+	node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+	ASSERT(node_blk != NULL);
 
-	if (ret != SEG_TYPE_NODE && ret != SEG_TYPE_CUR_NODE) {
-		ASSERT_MSG("Summary footer is not for node segment");
-		return -EINVAL;
-	}
+	if (!IS_VALID_NID(sbi, nid))
+		goto out;
 
-	if (le32_to_cpu(sum_entry.nid) != nid) {
-		DBG(0, "nid                       [0x%x]\n", nid);
-		DBG(0, "target blk_addr           [0x%x]\n", blk_addr);
-		DBG(0, "summary blk_addr          [0x%x]\n",
-					GET_SUM_BLKADDR(sbi,
-					GET_SEGNO(sbi, blk_addr)));
-		DBG(0, "seg no / offset           [0x%x / 0x%x]\n",
-					GET_SEGNO(sbi, blk_addr),
-					OFFSET_IN_SEG(sbi, blk_addr));
-		DBG(0, "summary_entry.nid         [0x%x]\n",
-					le32_to_cpu(sum_entry.nid));
-		DBG(0, "--> node block's nid      [0x%x]\n", nid);
-		ASSERT_MSG("Invalid node seg summary\n");
-		return -EINVAL;
-	}
-	return 0;
+	get_node_info(sbi, nid, &ni);
+
+	if (!IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+		goto out;
+
+	/* read node_block */
+	ret = dev_read_block(node_blk, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	if (le32_to_cpu(node_blk->footer.nid) != nid)
+		goto out;
+
+	/* check its block address */
+	if (node_blk->footer.nid == node_blk->footer.ino)
+		target_blk_addr = node_blk->i.i_addr[ofs_in_node];
+	else
+		target_blk_addr = node_blk->dn.addr[ofs_in_node];
+
+	if (blk_addr == le32_to_cpu(target_blk_addr))
+		ret = 1;
+out:
+	free(node_blk);
+	return ret;
 }
 
 static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
 		u32 parent_nid, u16 idx_in_node, u8 version)
 {
-	int ret = 0;
-	struct f2fs_summary sum_entry;
+	struct f2fs_summary_block *sum_blk;
+	struct f2fs_summary *sum_entry;
+	struct seg_entry * se;
+	u32 segno, offset;
+	int need_fix = 0, ret = 0;
+	int type;
 
-	ret = get_sum_entry(sbi, blk_addr, &sum_entry);
+	segno = GET_SEGNO(sbi, blk_addr);
+	offset = OFFSET_IN_SEG(sbi, blk_addr);
 
-	if (ret != SEG_TYPE_DATA && ret != SEG_TYPE_CUR_DATA) {
-		ASSERT_MSG("Summary footer is not for data segment");
-		return -EINVAL;
+	sum_blk = get_sum_block(sbi, segno, &type);
+
+	if (type != SEG_TYPE_DATA && type != SEG_TYPE_CUR_DATA) {
+		/* can't fix current summary, then drop the block */
+		if (!c.fix_on || type < 0) {
+			ASSERT_MSG("Summary footer is not for data segment");
+			ret = -EINVAL;
+			goto out;
+		}
+
+		need_fix = 1;
+		se = get_seg_entry(sbi, segno);
+		if (IS_DATASEG(se->type)) {
+			FIX_MSG("Summary footer indicates a data segment: 0x%x", segno);
+			sum_blk->footer.entry_type = SUM_TYPE_DATA;
+		} else {
+			ret = -EINVAL;
+			goto out;
+		}
 	}
 
-	if (le32_to_cpu(sum_entry.nid) != parent_nid ||
-			sum_entry.version != version ||
-			le16_to_cpu(sum_entry.ofs_in_node) != idx_in_node) {
+	sum_entry = &(sum_blk->entries[offset]);
 
-		DBG(0, "summary_entry.nid         [0x%x]\n",
-					le32_to_cpu(sum_entry.nid));
-		DBG(0, "summary_entry.version     [0x%x]\n",
-					sum_entry.version);
-		DBG(0, "summary_entry.ofs_in_node [0x%x]\n",
-					le16_to_cpu(sum_entry.ofs_in_node));
-		DBG(0, "parent nid                [0x%x]\n", parent_nid);
-		DBG(0, "version from nat          [0x%x]\n", version);
-		DBG(0, "idx in parent node        [0x%x]\n", idx_in_node);
+	if (le32_to_cpu(sum_entry->nid) != parent_nid ||
+			sum_entry->version != version ||
+			le16_to_cpu(sum_entry->ofs_in_node) != idx_in_node) {
+		if (!c.fix_on || type < 0) {
+			DBG(0, "summary_entry.nid         [0x%x]\n",
+					le32_to_cpu(sum_entry->nid));
+			DBG(0, "summary_entry.version     [0x%x]\n",
+					sum_entry->version);
+			DBG(0, "summary_entry.ofs_in_node [0x%x]\n",
+					le16_to_cpu(sum_entry->ofs_in_node));
+			DBG(0, "parent nid                [0x%x]\n",
+					parent_nid);
+			DBG(0, "version from nat          [0x%x]\n", version);
+			DBG(0, "idx in parent node        [0x%x]\n",
+					idx_in_node);
 
-		DBG(0, "Target data block addr    [0x%x]\n", blk_addr);
-		ASSERT_MSG("Invalid data seg summary\n");
-		return -EINVAL;
+			DBG(0, "Target data block addr    [0x%x]\n", blk_addr);
+			ASSERT_MSG("Invalid data seg summary\n");
+			ret = -EINVAL;
+		} else if (is_valid_summary(sbi, sum_entry, blk_addr)) {
+			/* delete wrong index */
+			ret = -EINVAL;
+		} else {
+			FIX_MSG("Set data summary 0x%x -> [0x%x] [0x%x] [0x%x]",
+					segno, parent_nid, version, idx_in_node);
+			sum_entry->nid = cpu_to_le32(parent_nid);
+			sum_entry->version = version;
+			sum_entry->ofs_in_node = cpu_to_le16(idx_in_node);
+			need_fix = 1;
+		}
 	}
+	if (need_fix && !c.ro) {
+		u64 ssa_blk;
+		int ret2;
+
+		ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+		ret2 = dev_write_block(sum_blk, ssa_blk);
+		ASSERT(ret2 >= 0);
+	}
+out:
+	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+					type == SEG_TYPE_MAX)
+		free(sum_blk);
+	return ret;
+}
+
+static int __check_inode_mode(u32 nid, enum FILE_TYPE ftype, u32 mode)
+{
+	if (ftype >= F2FS_FT_MAX)
+		return 0;
+	if (S_ISLNK(mode) && ftype != F2FS_FT_SYMLINK)
+		goto err;
+	if (S_ISREG(mode) && ftype != F2FS_FT_REG_FILE)
+		goto err;
+	if (S_ISDIR(mode) && ftype != F2FS_FT_DIR)
+		goto err;
+	if (S_ISCHR(mode) && ftype != F2FS_FT_CHRDEV)
+		goto err;
+	if (S_ISBLK(mode) && ftype != F2FS_FT_BLKDEV)
+		goto err;
+	if (S_ISFIFO(mode) && ftype != F2FS_FT_FIFO)
+		goto err;
+	if (S_ISSOCK(mode) && ftype != F2FS_FT_SOCK)
+		goto err;
 	return 0;
+err:
+	ASSERT_MSG("mismatch i_mode [0x%x] [0x%x vs. 0x%x]", nid, ftype, mode);
+	return -1;
 }
 
 static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid,
 			struct f2fs_node *node_blk,
 			enum FILE_TYPE ftype, enum NODE_TYPE ntype,
-			struct node_info *ni)
+			struct node_info *ni, u8 *name)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	int ret;
@@ -203,18 +358,18 @@
 	}
 
 	get_node_info(sbi, nid, ni);
+	if (ni->ino == 0) {
+		ASSERT_MSG("nid[0x%x] ino is 0", nid);
+		return -EINVAL;
+	}
+
 	if (ni->blk_addr == NEW_ADDR) {
 		ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid);
 		return -EINVAL;
 	}
 
 	if (!IS_VALID_BLK_ADDR(sbi, ni->blk_addr)) {
-		ASSERT_MSG("blkaddres is not valid. [0x%x]", ni->blk_addr);
-		return -EINVAL;
-	}
-
-	if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) {
-		ASSERT_MSG("summary node block is not valid. [0x%x]", nid);
+		ASSERT_MSG("blkaddress is not valid. [0x%x]", ni->blk_addr);
 		return -EINVAL;
 	}
 
@@ -228,6 +383,11 @@
 				le32_to_cpu(node_blk->footer.ino));
 		return -EINVAL;
 	}
+	if (ni->ino != le32_to_cpu(node_blk->footer.ino)) {
+		ASSERT_MSG("nid[0x%x] nat_entry->ino[0x%x] footer.ino[0x%x]",
+				nid, ni->ino, le32_to_cpu(node_blk->footer.ino));
+		return -EINVAL;
+	}
 	if (ntype != TYPE_INODE &&
 			node_blk->footer.nid == node_blk->footer.ino) {
 		ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]",
@@ -263,6 +423,14 @@
 		}
 	}
 
+	/* this if only from fix_hard_links */
+	if (ftype == F2FS_FT_MAX)
+		return 0;
+
+	if (ntype == TYPE_INODE &&
+		__check_inode_mode(nid, ftype, le32_to_cpu(node_blk->i.i_mode)))
+		return -EINVAL;
+
 	/* workaround to fix later */
 	if (ftype != F2FS_FT_ORPHAN ||
 			f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0)
@@ -271,6 +439,11 @@
 		ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n",
 				nid);
 
+	if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) {
+		ASSERT_MSG("summary node block is not valid. [0x%x]", nid);
+		return -EINVAL;
+	}
+
 	if (f2fs_test_sit_bitmap(sbi, ni->blk_addr) == 0)
 		ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]",
 				ni->blk_addr);
@@ -297,7 +470,7 @@
 
 	/* Sanity check */
 	if (sanity_check_nid(sbi, x_nid, node_blk,
-				F2FS_FT_XATTR, TYPE_XATTR, &ni)) {
+				F2FS_FT_XATTR, TYPE_XATTR, &ni, NULL)) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -311,8 +484,8 @@
 }
 
 int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
-		u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
-		u32 *blk_cnt)
+		u32 nid, u8 *name, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
+		u32 *blk_cnt, struct child_info *child)
 {
 	struct node_info ni;
 	struct f2fs_node *node_blk = NULL;
@@ -320,7 +493,7 @@
 	node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
 	ASSERT(node_blk != NULL);
 
-	if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni))
+	if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni, name))
 		goto err;
 
 	if (ntype == TYPE_INODE) {
@@ -331,19 +504,19 @@
 			f2fs_set_main_bitmap(sbi, ni.blk_addr,
 							CURSEG_WARM_NODE);
 			fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk,
-					blk_cnt, &ni);
+					blk_cnt, child, &ni);
 			break;
 		case TYPE_INDIRECT_NODE:
 			f2fs_set_main_bitmap(sbi, ni.blk_addr,
 							CURSEG_COLD_NODE);
 			fsck_chk_idnode_blk(sbi, inode, ftype, node_blk,
-					blk_cnt);
+					blk_cnt, child);
 			break;
 		case TYPE_DOUBLE_INDIRECT_NODE:
 			f2fs_set_main_bitmap(sbi, ni.blk_addr,
 							CURSEG_COLD_NODE);
 			fsck_chk_didnode_blk(sbi, inode, ftype, node_blk,
-					blk_cnt);
+					blk_cnt, child);
 			break;
 		default:
 			ASSERT(0);
@@ -356,20 +529,87 @@
 	return -EINVAL;
 }
 
+static inline void get_extent_info(struct extent_info *ext,
+					struct f2fs_extent *i_ext)
+{
+	ext->fofs = le32_to_cpu(i_ext->fofs);
+	ext->blk = le32_to_cpu(i_ext->blk_addr);
+	ext->len = le32_to_cpu(i_ext->len);
+}
+
+static void check_extent_info(struct child_info *child,
+						block_t blkaddr, int last)
+{
+	struct extent_info *ei = &child->ei;
+	u32 pgofs = child->pgofs;
+	int is_hole = 0;
+
+	if (!ei->len)
+		return;
+
+	if (child->state & FSCK_UNMATCHED_EXTENT)
+		return;
+
+	if (last) {
+		/* hole exist in the back of extent */
+		if (child->last_blk != ei->blk + ei->len - 1)
+			child->state |= FSCK_UNMATCHED_EXTENT;
+		return;
+	}
+
+	if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR)
+		is_hole = 1;
+
+	if (pgofs >= ei->fofs && pgofs < ei->fofs + ei->len) {
+		/* unmatched blkaddr */
+		if (is_hole || (blkaddr != pgofs - ei->fofs + ei->blk))
+			goto unmatched;
+
+		if (!child->last_blk) {
+			/* hole exists in the front of extent */
+			if (pgofs != ei->fofs)
+				goto unmatched;
+		} else if (child->last_blk + 1 != blkaddr) {
+			/* hole exists in the middle of extent */
+			goto unmatched;
+		}
+		child->last_blk = blkaddr;
+		return;
+	}
+
+	if (is_hole)
+		return;
+
+	if (blkaddr < ei->blk || blkaddr >= ei->blk + ei->len)
+		return;
+	/* unmatched file offset */
+unmatched:
+	child->state |= FSCK_UNMATCHED_EXTENT;
+}
+
 /* start with valid nid and blkaddr */
 void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 		enum FILE_TYPE ftype, struct f2fs_node *node_blk,
 		u32 *blk_cnt, struct node_info *ni)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
-	u32 child_cnt = 0, child_files = 0;
+	struct child_info child;
 	enum NODE_TYPE ntype;
 	u32 i_links = le32_to_cpu(node_blk->i.i_links);
+	u64 i_size = le64_to_cpu(node_blk->i.i_size);
 	u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks);
+	unsigned char en[F2FS_NAME_LEN + 1];
+	int namelen;
 	unsigned int idx = 0;
 	int need_fix = 0;
 	int ret;
 
+	memset(&child, 0, sizeof(child));
+	child.links = 2;
+	child.p_ino = nid;
+	child.pp_ino = le32_to_cpu(node_blk->i.i_pino);
+	child.dir_level = node_blk->i.i_dir_level;
+
 	if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0)
 		fsck->chk.valid_inode_cnt++;
 
@@ -379,7 +619,7 @@
 		if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
 			f2fs_set_main_bitmap(sbi, ni->blk_addr,
 							CURSEG_WARM_NODE);
-			if (i_links > 1) {
+			if (i_links > 1 && ftype != F2FS_FT_ORPHAN) {
 				/* First time. Create new hard link node */
 				add_into_hard_link_list(sbi, nid, i_links);
 				fsck->chk.multi_hard_link_files++;
@@ -389,7 +629,7 @@
 			if (find_and_dec_hard_link_list(sbi, nid)) {
 				ASSERT_MSG("[0x%x] needs more i_links=0x%x",
 						nid, i_links);
-				if (config.fix_on) {
+				if (c.fix_on) {
 					node_blk->i.i_links =
 						cpu_to_le32(i_links + 1);
 					need_fix = 1;
@@ -397,7 +637,7 @@
 						"i_links= 0x%x -> 0x%x",
 						nid, i_links, i_links + 1);
 				}
-				goto check;
+				goto skip_blkcnt_fix;
 			}
 			/* No need to go deep into the node */
 			return;
@@ -406,7 +646,7 @@
 
 	if (fsck_chk_xattr_blk(sbi, nid,
 			le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt) &&
-			config.fix_on) {
+			c.fix_on) {
 		node_blk->i.i_xattr_nid = 0;
 		need_fix = 1;
 		FIX_MSG("Remove xattr block: 0x%x, x_nid = 0x%x",
@@ -442,8 +682,7 @@
 	}
 	if((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) {
 		DBG(3, "ino[0x%x] has inline dentry!\n", nid);
-		ret = fsck_chk_inline_dentries(sbi, node_blk,
-					&child_cnt, &child_files);
+		ret = fsck_chk_inline_dentries(sbi, node_blk, &child);
 		if (ret < 0) {
 			/* should fix this bug all the time */
 			need_fix = 1;
@@ -464,17 +703,27 @@
 		}
 	}
 
+	/* init extent info */
+	get_extent_info(&child.ei, &node_blk->i.i_ext);
+	child.last_blk = 0;
+
 	/* check data blocks in inode */
-	for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) {
-		if (le32_to_cpu(node_blk->i.i_addr[idx]) != 0) {
+	for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i);
+						idx++, child.pgofs++) {
+		block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[idx]);
+
+		/* check extent info */
+		check_extent_info(&child, blkaddr, 0);
+
+		if (blkaddr != 0) {
 			ret = fsck_chk_data_blk(sbi,
-					le32_to_cpu(node_blk->i.i_addr[idx]),
-					&child_cnt, &child_files,
-					(i_blocks == *blk_cnt),
-					ftype, nid, idx, ni->version);
+					blkaddr,
+					&child, (i_blocks == *blk_cnt),
+					ftype, nid, idx, ni->version,
+					file_is_encrypt(&node_blk->i));
 			if (!ret) {
 				*blk_cnt = *blk_cnt + 1;
-			} else if (config.fix_on) {
+			} else if (c.fix_on) {
 				node_blk->i.i_addr[idx] = 0;
 				need_fix = 1;
 				FIX_MSG("[0x%x] i_addr[%d] = 0", nid, idx);
@@ -484,6 +733,8 @@
 
 	/* check node blocks in inode */
 	for (idx = 0; idx < 5; idx++) {
+		nid_t i_nid = le32_to_cpu(node_blk->i.i_nid[idx]);
+
 		if (idx == 0 || idx == 1)
 			ntype = TYPE_DIRECT_NODE;
 		else if (idx == 2 || idx == 3)
@@ -493,58 +744,115 @@
 		else
 			ASSERT(0);
 
-		if (le32_to_cpu(node_blk->i.i_nid[idx]) != 0) {
-			ret = fsck_chk_node_blk(sbi, &node_blk->i,
-					le32_to_cpu(node_blk->i.i_nid[idx]),
-					ftype, ntype, blk_cnt);
-			if (!ret) {
-				*blk_cnt = *blk_cnt + 1;
-			} else if (config.fix_on) {
+		if (i_nid == 0x0)
+			goto skip;
+
+		ret = fsck_chk_node_blk(sbi, &node_blk->i, i_nid,
+				NULL, ftype, ntype, blk_cnt, &child);
+		if (!ret) {
+			*blk_cnt = *blk_cnt + 1;
+		} else if (ret == -EINVAL) {
+			if (c.fix_on) {
 				node_blk->i.i_nid[idx] = 0;
 				need_fix = 1;
 				FIX_MSG("[0x%x] i_nid[%d] = 0", nid, idx);
 			}
+skip:
+			if (ntype == TYPE_DIRECT_NODE)
+				child.pgofs += ADDRS_PER_BLOCK;
+			else if (ntype == TYPE_INDIRECT_NODE)
+				child.pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
+			else
+				child.pgofs += ADDRS_PER_BLOCK *
+						NIDS_PER_BLOCK * NIDS_PER_BLOCK;
 		}
+
+	}
+
+	/* check uncovered range in the back of extent */
+	check_extent_info(&child, 0, 1);
+
+	if (child.state & FSCK_UNMATCHED_EXTENT) {
+		ASSERT_MSG("ino: 0x%x has wrong ext: [pgofs:%u, blk:%u, len:%u]",
+				nid, child.ei.fofs, child.ei.blk, child.ei.len);
+		if (c.fix_on)
+			need_fix = 1;
 	}
 check:
-	if (ftype == F2FS_FT_DIR)
-		DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n",
-				le32_to_cpu(node_blk->footer.ino),
-				node_blk->i.i_name,
-				le32_to_cpu(node_blk->i.i_current_depth),
-				child_files);
-	if (ftype == F2FS_FT_ORPHAN)
-		DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n",
-				le32_to_cpu(node_blk->footer.ino),
-				node_blk->i.i_name,
-				(u32)i_blocks);
-
 	if (i_blocks != *blk_cnt) {
 		ASSERT_MSG("ino: 0x%x has i_blocks: %08"PRIx64", "
 				"but has %u blocks",
 				nid, i_blocks, *blk_cnt);
-		if (config.fix_on) {
+		if (c.fix_on) {
 			node_blk->i.i_blocks = cpu_to_le64(*blk_cnt);
 			need_fix = 1;
 			FIX_MSG("[0x%x] i_blocks=0x%08"PRIx64" -> 0x%x",
 					nid, i_blocks, *blk_cnt);
 		}
 	}
-	if (ftype == F2FS_FT_DIR && i_links != child_cnt) {
-		ASSERT_MSG("ino: 0x%x has i_links: %u but real links: %u",
-				nid, i_links, child_cnt);
-		if (config.fix_on) {
-			node_blk->i.i_links = cpu_to_le32(child_cnt);
+skip_blkcnt_fix:
+	namelen = convert_encrypted_name(node_blk->i.i_name,
+					le32_to_cpu(node_blk->i.i_namelen),
+					en, file_is_encrypt(&node_blk->i));
+	en[namelen] = '\0';
+	if (ftype == F2FS_FT_ORPHAN)
+		DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n",
+				le32_to_cpu(node_blk->footer.ino),
+				en, (u32)i_blocks);
+
+	if (ftype == F2FS_FT_DIR) {
+		DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n",
+				le32_to_cpu(node_blk->footer.ino), en,
+				le32_to_cpu(node_blk->i.i_current_depth),
+				child.files);
+
+		if (i_links != child.links) {
+			ASSERT_MSG("ino: 0x%x i_links: %u, real links: %u",
+					nid, i_links, child.links);
+			if (c.fix_on) {
+				node_blk->i.i_links = cpu_to_le32(child.links);
+				need_fix = 1;
+				FIX_MSG("Dir: 0x%x i_links= 0x%x -> 0x%x",
+						nid, i_links, child.links);
+			}
+		}
+		if (child.dots < 2 &&
+				!(node_blk->i.i_inline & F2FS_INLINE_DOTS)) {
+			ASSERT_MSG("ino: 0x%x dots: %u",
+					nid, child.dots);
+			if (c.fix_on) {
+				node_blk->i.i_inline |= F2FS_INLINE_DOTS;
+				need_fix = 1;
+				FIX_MSG("Dir: 0x%x set inline_dots", nid);
+			}
+		}
+	}
+	if (ftype == F2FS_FT_SYMLINK && i_blocks && i_size == 0) {
+		DBG(1, "ino: 0x%x i_blocks: %lu with zero i_size",
+							nid, i_blocks);
+		if (c.fix_on) {
+			u64 i_size = i_blocks * F2FS_BLKSIZE;
+
+			node_blk->i.i_size = cpu_to_le64(i_size);
 			need_fix = 1;
-			FIX_MSG("Dir: 0x%x i_links= 0x%x -> 0x%x",
-						nid, i_links, child_cnt);
+			FIX_MSG("Symlink: recover 0x%x with i_size=%lu",
+							nid, i_size);
 		}
 	}
 
-	if (ftype == F2FS_FT_ORPHAN && i_links)
-		ASSERT_MSG("ino: 0x%x is orphan inode, but has i_links: %u",
+	if (ftype == F2FS_FT_ORPHAN && i_links) {
+		MSG(0, "ino: 0x%x is orphan inode, but has i_links: %u",
 				nid, i_links);
-	if (need_fix) {
+		if (c.fix_on) {
+			node_blk->i.i_links = 0;
+			need_fix = 1;
+			FIX_MSG("ino: 0x%x orphan_inode, i_links= 0x%x -> 0",
+					nid, i_links);
+		}
+	}
+	if (need_fix && !c.ro) {
+		/* drop extent information to avoid potential wrong access */
+		node_blk->i.i_ext.len = 0;
 		ret = dev_write_block(node_blk, ni->blk_addr);
 		ASSERT(ret >= 0);
 	}
@@ -552,29 +860,34 @@
 
 int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 		u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk,
-		u32 *blk_cnt, struct node_info *ni)
+		u32 *blk_cnt, struct child_info *child, struct node_info *ni)
 {
 	int idx, ret;
-	u32 child_cnt = 0, child_files = 0;
 	int need_fix = 0;
+	child->p_ino = nid;
+	child->pp_ino = le32_to_cpu(inode->i_pino);
 
-	for (idx = 0; idx < ADDRS_PER_BLOCK; idx++) {
-		if (le32_to_cpu(node_blk->dn.addr[idx]) == 0x0)
+	for (idx = 0; idx < ADDRS_PER_BLOCK; idx++, child->pgofs++) {
+		block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]);
+
+		check_extent_info(child, blkaddr, 0);
+
+		if (blkaddr == 0x0)
 			continue;
 		ret = fsck_chk_data_blk(sbi,
-			le32_to_cpu(node_blk->dn.addr[idx]),
-			&child_cnt, &child_files,
+			blkaddr, child,
 			le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
-			nid, idx, ni->version);
+			nid, idx, ni->version,
+			file_is_encrypt(inode));
 		if (!ret) {
 			*blk_cnt = *blk_cnt + 1;
-		} else if (config.fix_on) {
+		} else if (c.fix_on) {
 			node_blk->dn.addr[idx] = 0;
 			need_fix = 1;
 			FIX_MSG("[0x%x] dn.addr[%d] = 0", nid, idx);
 		}
 	}
-	if (need_fix) {
+	if (need_fix && !c.ro) {
 		ret = dev_write_block(node_blk, ni->blk_addr);
 		ASSERT(ret >= 0);
 	}
@@ -582,69 +895,154 @@
 }
 
 int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
-		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt)
+		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
+		struct child_info *child)
 {
-	int ret;
+	int need_fix = 0, ret;
 	int i = 0;
 
-	for (i = 0 ; i < NIDS_PER_BLOCK; i++) {
+	for (i = 0; i < NIDS_PER_BLOCK; i++) {
 		if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
-			continue;
+			goto skip;
 		ret = fsck_chk_node_blk(sbi, inode,
-				le32_to_cpu(node_blk->in.nid[i]),
-				ftype, TYPE_DIRECT_NODE, blk_cnt);
+				le32_to_cpu(node_blk->in.nid[i]), NULL,
+				ftype, TYPE_DIRECT_NODE, blk_cnt, child);
 		if (!ret)
 			*blk_cnt = *blk_cnt + 1;
-		else if (ret == -EINVAL)
-			printf("delete in.nid[i] = 0;\n");
+		else if (ret == -EINVAL) {
+			if (!c.fix_on)
+				printf("should delete in.nid[i] = 0;\n");
+			else {
+				node_blk->in.nid[i] = 0;
+				need_fix = 1;
+				FIX_MSG("Set indirect node 0x%x -> 0\n", i);
+			}
+skip:
+			child->pgofs += ADDRS_PER_BLOCK;
+		}
 	}
+
+	if (need_fix && !c.ro) {
+		struct node_info ni;
+		nid_t nid = le32_to_cpu(node_blk->footer.nid);
+
+		get_node_info(sbi, nid, &ni);
+		ret = dev_write_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
 	return 0;
 }
 
 int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
-		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt)
+		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
+		struct child_info *child)
 {
 	int i = 0;
-	int ret = 0;
+	int need_fix = 0, ret = 0;
 
 	for (i = 0; i < NIDS_PER_BLOCK; i++) {
 		if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
-			continue;
+			goto skip;
 		ret = fsck_chk_node_blk(sbi, inode,
-				le32_to_cpu(node_blk->in.nid[i]),
-				ftype, TYPE_INDIRECT_NODE, blk_cnt);
+				le32_to_cpu(node_blk->in.nid[i]), NULL,
+				ftype, TYPE_INDIRECT_NODE, blk_cnt, child);
 		if (!ret)
 			*blk_cnt = *blk_cnt + 1;
-		else if (ret == -EINVAL)
-			printf("delete in.nid[i] = 0;\n");
+		else if (ret == -EINVAL) {
+			if (!c.fix_on)
+				printf("should delete in.nid[i] = 0;\n");
+			else {
+				node_blk->in.nid[i] = 0;
+				need_fix = 1;
+				FIX_MSG("Set double indirect node 0x%x -> 0\n", i);
+			}
+skip:
+			child->pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
+		}
 	}
+
+	if (need_fix && !c.ro) {
+		struct node_info ni;
+		nid_t nid = le32_to_cpu(node_blk->footer.nid);
+
+		get_node_info(sbi, nid, &ni);
+		ret = dev_write_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
 	return 0;
 }
 
+static const char *lookup_table =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+
+/**
+ * digest_encode() -
+ *
+ * Encodes the input digest using characters from the set [a-zA-Z0-9_+].
+ * The encoded string is roughly 4/3 times the size of the input string.
+ */
+static int digest_encode(const char *src, int len, char *dst)
+{
+	int i = 0, bits = 0, ac = 0;
+	char *cp = dst;
+
+	while (i < len) {
+		ac += (((unsigned char) src[i]) << bits);
+		bits += 8;
+		do {
+			*cp++ = lookup_table[ac & 0x3f];
+			ac >>= 6;
+			bits -= 6;
+		} while (bits >= 6);
+		i++;
+	}
+	if (bits)
+		*cp++ = lookup_table[ac & 0x3f];
+	*cp = 0;
+	return cp - dst;
+}
+
+int convert_encrypted_name(unsigned char *name, int len,
+				unsigned char *new, int encrypted)
+{
+	if (!encrypted) {
+		memcpy(new, name, len);
+		new[len] = 0;
+		return len;
+	}
+
+	*new = '_';
+	return digest_encode((const char *)name, 24, (char *)new + 1);
+}
+
 static void print_dentry(__u32 depth, __u8 *name,
-		unsigned long *bitmap,
-		struct f2fs_dir_entry *dentry,
-		int max, int idx, int last_blk)
+		u8 *bitmap, struct f2fs_dir_entry *dentry,
+		int max, int idx, int last_blk, int encrypted)
 {
 	int last_de = 0;
 	int next_idx = 0;
 	int name_len;
 	unsigned int i;
 	int bit_offset;
+	unsigned char new[F2FS_NAME_LEN + 1];
 
-	if (config.dbg_lv != -1)
+	if (c.dbg_lv != -1)
 		return;
 
 	name_len = le16_to_cpu(dentry[idx].name_len);
 	next_idx = idx + (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
 
-	bit_offset = find_next_bit(bitmap, max, next_idx);
+	bit_offset = find_next_bit_le(bitmap, max, next_idx);
 	if (bit_offset >= max && last_blk)
 		last_de = 1;
 
 	if (tree_mark_size <= depth) {
 		tree_mark_size *= 2;
+		ASSERT(tree_mark_size != 0);
 		tree_mark = realloc(tree_mark, tree_mark_size);
+		ASSERT(tree_mark != NULL);
 	}
 
 	if (last_de)
@@ -655,144 +1053,333 @@
 	if (tree_mark[depth - 1] == '`')
 		tree_mark[depth - 1] = ' ';
 
-
 	for (i = 1; i < depth; i++)
 		printf("%c   ", tree_mark[i]);
-	printf("%c-- %s 0x%x\n", last_de ? '`' : '|',
-				name, le32_to_cpu(dentry[idx].ino));
+
+	convert_encrypted_name(name, name_len, new, encrypted);
+
+	printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n",
+			last_de ? '`' : '|',
+			new, le32_to_cpu(dentry[idx].ino),
+			encrypted);
 }
 
-static int __chk_dentries(struct f2fs_sb_info *sbi, u32 *child_cnt,
-			u32* child_files,
-			unsigned long *bitmap,
-			struct f2fs_dir_entry *dentry,
+static int f2fs_check_hash_code(struct f2fs_dir_entry *dentry,
+			const unsigned char *name, u32 len, int encrypted)
+{
+	f2fs_hash_t hash_code = f2fs_dentry_hash(name, len);
+
+	/* fix hash_code made by old buggy code */
+	if (dentry->hash_code != hash_code) {
+		unsigned char new[F2FS_NAME_LEN + 1];
+
+		convert_encrypted_name((unsigned char *)name, len,
+							new, encrypted);
+		FIX_MSG("Mismatch hash_code for \"%s\" [%x:%x]",
+				new, le32_to_cpu(dentry->hash_code),
+				hash_code);
+		dentry->hash_code = cpu_to_le32(hash_code);
+		return 1;
+	}
+	return 0;
+}
+
+static unsigned int dir_buckets(unsigned int level, int dir_level)
+{
+	if (level + dir_level < MAX_DIR_HASH_DEPTH / 2)
+		return 1 << (level + dir_level);
+	else
+		return MAX_DIR_BUCKETS;
+}
+
+static unsigned int bucket_blocks(unsigned int level)
+{
+	if (level < MAX_DIR_HASH_DEPTH / 2)
+		return 2;
+	else
+		return 4;
+}
+
+static unsigned long dir_block_index(unsigned int level,
+				int dir_level, unsigned int idx)
+{
+	unsigned long i;
+	unsigned long bidx = 0;
+
+	for (i = 0; i < level; i++)
+		bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
+	bidx += idx * bucket_blocks(level);
+	return bidx;
+}
+
+static int __get_current_level(int dir_level, u32 pgofs)
+{
+	unsigned int bidx = 0;
+	int i;
+
+	for (i = 0; i < MAX_DIR_HASH_DEPTH; i++) {
+		bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
+		if (bidx > pgofs)
+			break;
+	}
+	return i;
+}
+
+static int f2fs_check_dirent_position(u8 *name, u16 name_len, u32 pgofs,
+						u8 dir_level, u32 pino)
+{
+	f2fs_hash_t namehash = f2fs_dentry_hash(name, name_len);
+	unsigned int nbucket, nblock;
+	unsigned int bidx, end_block;
+	int level;
+
+	level = __get_current_level(dir_level, pgofs);
+
+	nbucket = dir_buckets(level, dir_level);
+	nblock = bucket_blocks(level);
+
+	bidx = dir_block_index(level, dir_level,
+					le32_to_cpu(namehash) % nbucket);
+	end_block = bidx + nblock;
+
+	if (pgofs >= bidx && pgofs < end_block)
+		return 0;
+
+	ASSERT_MSG("Wrong position of dirent pino:%u, name:%s, level:%d, "
+		"dir_level:%d, pgofs:%u, correct range:[%u, %u]\n",
+		pino, name, level, dir_level, pgofs, bidx, end_block - 1);
+	return 1;
+}
+
+static int __chk_dots_dentries(struct f2fs_sb_info *sbi,
+			       struct f2fs_dir_entry *dentry,
+			       struct child_info *child,
+			       u8 *name, int len,
+			       __u8 (*filename)[F2FS_SLOT_LEN],
+			       int encrypted)
+{
+	int fixed = 0;
+
+	if ((name[0] == '.' && len == 1)) {
+		if (le32_to_cpu(dentry->ino) != child->p_ino) {
+			ASSERT_MSG("Bad inode number[0x%x] for '.', parent_ino is [0x%x]\n",
+				le32_to_cpu(dentry->ino), child->p_ino);
+			dentry->ino = cpu_to_le32(child->p_ino);
+			fixed = 1;
+		}
+	}
+
+	if (name[0] == '.' && name[1] == '.' && len == 2) {
+		if (child->p_ino == F2FS_ROOT_INO(sbi)) {
+			if (le32_to_cpu(dentry->ino) != F2FS_ROOT_INO(sbi)) {
+				ASSERT_MSG("Bad inode number[0x%x] for '..'\n",
+					le32_to_cpu(dentry->ino));
+				dentry->ino = cpu_to_le32(F2FS_ROOT_INO(sbi));
+				fixed = 1;
+			}
+		} else if (le32_to_cpu(dentry->ino) != child->pp_ino) {
+			ASSERT_MSG("Bad inode number[0x%x] for '..', parent parent ino is [0x%x]\n",
+				le32_to_cpu(dentry->ino), child->pp_ino);
+			dentry->ino = cpu_to_le32(child->pp_ino);
+			fixed = 1;
+		}
+	}
+
+	if (f2fs_check_hash_code(dentry, name, len, encrypted))
+		fixed = 1;
+
+	if (name[len] != '\0') {
+		ASSERT_MSG("'.' is not NULL terminated\n");
+		name[len] = '\0';
+		memcpy(*filename, name, len);
+		fixed = 1;
+	}
+	return fixed;
+}
+
+static void nullify_dentry(struct f2fs_dir_entry *dentry, int offs,
+			   __u8 (*filename)[F2FS_SLOT_LEN], u8 **bitmap)
+{
+	memset(dentry, 0, sizeof(struct f2fs_dir_entry));
+	test_and_clear_bit_le(offs, *bitmap);
+	memset(*filename, 0, F2FS_SLOT_LEN);
+}
+
+static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
+			u8 *bitmap, struct f2fs_dir_entry *dentry,
 			__u8 (*filenames)[F2FS_SLOT_LEN],
-			int max, int last_blk)
+			int max, int last_blk, int encrypted)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	enum FILE_TYPE ftype;
 	int dentries = 0;
 	u32 blk_cnt;
 	u8 *name;
-	u32 hash_code, ino;
-	u16 name_len;;
+	unsigned char en[F2FS_NAME_LEN + 1];
+	u16 name_len, en_len;
 	int ret = 0;
 	int fixed = 0;
-	int i;
+	int i, slots;
 
 	/* readahead inode blocks */
-	for (i = 0; i < max;) {
-		if (test_bit(i, bitmap) == 0) {
-			i++;
+	for (i = 0; i < max; i++) {
+		u32 ino;
+
+		if (test_bit_le(i, bitmap) == 0)
 			continue;
-		}
+
 		ino = le32_to_cpu(dentry[i].ino);
 
 		if (IS_VALID_NID(sbi, ino)) {
 			struct node_info ni;
 
 			get_node_info(sbi, ino, &ni);
-			if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+			if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) {
 				dev_reada_block(ni.blk_addr);
+				name_len = le16_to_cpu(dentry[i].name_len);
+				if (name_len > 0)
+					i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN - 1;
+			}
 		}
-		name_len = le16_to_cpu(dentry[i].name_len);
-		i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
 	}
 
 	for (i = 0; i < max;) {
-		if (test_bit(i, bitmap) == 0) {
+		if (test_bit_le(i, bitmap) == 0) {
 			i++;
 			continue;
 		}
 		if (!IS_VALID_NID(sbi, le32_to_cpu(dentry[i].ino))) {
-			DBG(1, "Bad dentry 0x%x with invalid NID/ino 0x%x",
-			    i, le32_to_cpu(dentry[i].ino));
-			if (config.fix_on) {
+			ASSERT_MSG("Bad dentry 0x%x with invalid NID/ino 0x%x",
+				    i, le32_to_cpu(dentry[i].ino));
+			if (c.fix_on) {
 				FIX_MSG("Clear bad dentry 0x%x with bad ino 0x%x",
 					i, le32_to_cpu(dentry[i].ino));
-				clear_bit(i, bitmap);
-				i++;
+				test_and_clear_bit_le(i, bitmap);
 				fixed = 1;
-				continue;
 			}
+			i++;
+			continue;
 		}
+
 		ftype = dentry[i].file_type;
-		if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE) && config.fix_on) {
-			DBG(1, "Bad dentry 0x%x with unexpected ftype 0x%x",
-			    i, ftype);
-			if (config.fix_on) {
+		if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE)) {
+			ASSERT_MSG("Bad dentry 0x%x with unexpected ftype 0x%x",
+						le32_to_cpu(dentry[i].ino), ftype);
+			if (c.fix_on) {
 				FIX_MSG("Clear bad dentry 0x%x with bad ftype 0x%x",
 					i, ftype);
-				clear_bit(i, bitmap);
-				i++;
+				test_and_clear_bit_le(i, bitmap);
 				fixed = 1;
-				continue;
 			}
+			i++;
+			continue;
 		}
+
 		name_len = le16_to_cpu(dentry[i].name_len);
+
+		if (name_len == 0 || name_len > F2FS_NAME_LEN) {
+			ASSERT_MSG("Bad dentry 0x%x with invalid name_len", i);
+			if (c.fix_on) {
+				FIX_MSG("Clear bad dentry 0x%x", i);
+				test_and_clear_bit_le(i, bitmap);
+				fixed = 1;
+			}
+			i++;
+			continue;
+		}
 		name = calloc(name_len + 1, 1);
 		memcpy(name, filenames[i], name_len);
-		hash_code = f2fs_dentry_hash((const unsigned char *)name,
-								name_len);
-
-		/* fix hash_code made by old buggy code */
-		if (le32_to_cpu(dentry[i].hash_code) != hash_code) {
-			dentry[i].hash_code = hash_code;
-			fixed = 1;
-			FIX_MSG("hash_code[%d] of %s", i, name);
-		}
+		slots = (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
 
 		/* Becareful. 'dentry.file_type' is not imode. */
 		if (ftype == F2FS_FT_DIR) {
-			*child_cnt = *child_cnt + 1;
 			if ((name[0] == '.' && name_len == 1) ||
 				(name[0] == '.' && name[1] == '.' &&
 							name_len == 2)) {
+				ret = __chk_dots_dentries(sbi, &dentry[i],
+					child, name, name_len, &filenames[i],
+					encrypted);
+				switch (ret) {
+				case 1:
+					fixed = 1;
+				case 0:
+					child->dots++;
+					break;
+				}
+
+				if (child->dots > 2) {
+					ASSERT_MSG("More than one '.' or '..', should delete the extra one\n");
+					nullify_dentry(&dentry[i], i,
+						       &filenames[i], &bitmap);
+					child->dots--;
+					fixed = 1;
+				}
+
 				i++;
 				free(name);
 				continue;
 			}
 		}
 
+		if (f2fs_check_hash_code(dentry + i, name, name_len, encrypted))
+			fixed = 1;
+
+		if (max == NR_DENTRY_IN_BLOCK) {
+			ret = f2fs_check_dirent_position(name, name_len,
+					child->pgofs,
+					child->dir_level, child->p_ino);
+			if (ret) {
+				if (c.fix_on) {
+					FIX_MSG("Clear bad dentry 0x%x", i);
+					test_and_clear_bit_le(i, bitmap);
+					fixed = 1;
+				}
+				i++;
+				free(name);
+				continue;
+			}
+		}
+
+		en_len = convert_encrypted_name(name, name_len, en, encrypted);
+		en[en_len] = '\0';
 		DBG(1, "[%3u]-[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n",
-				fsck->dentry_depth, i, name, name_len,
+				fsck->dentry_depth, i, en, name_len,
 				le32_to_cpu(dentry[i].ino),
 				dentry[i].file_type);
 
 		print_dentry(fsck->dentry_depth, name, bitmap,
-						dentry, max, i, last_blk);
+				dentry, max, i, last_blk, encrypted);
 
 		blk_cnt = 1;
 		ret = fsck_chk_node_blk(sbi,
-				NULL, le32_to_cpu(dentry[i].ino),
-				ftype, TYPE_INODE, &blk_cnt);
+				NULL, le32_to_cpu(dentry[i].ino), name,
+				ftype, TYPE_INODE, &blk_cnt, NULL);
 
-		if (ret && config.fix_on) {
+		if (ret && c.fix_on) {
 			int j;
-			int slots = (name_len + F2FS_SLOT_LEN - 1) /
-				F2FS_SLOT_LEN;
+
 			for (j = 0; j < slots; j++)
-				clear_bit(i + j, bitmap);
+				test_and_clear_bit_le(i + j, bitmap);
 			FIX_MSG("Unlink [0x%x] - %s len[0x%x], type[0x%x]",
 					le32_to_cpu(dentry[i].ino),
 					name, name_len,
 					dentry[i].file_type);
-			i += slots;
-			free(name);
 			fixed = 1;
-			continue;
+		} else if (ret == 0) {
+			if (ftype == F2FS_FT_DIR)
+				child->links++;
+			dentries++;
+			child->files++;
 		}
 
-		i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
-		dentries++;
-		*child_files = *child_files + 1;
+		i += slots;
 		free(name);
 	}
 	return fixed ? -1 : dentries;
 }
 
 int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
-		struct f2fs_node *node_blk, u32 *child_cnt, u32 *child_files)
+		struct f2fs_node *node_blk, struct child_info *child)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	struct f2fs_inline_dentry *de_blk;
@@ -802,10 +1389,11 @@
 	ASSERT(de_blk != NULL);
 
 	fsck->dentry_depth++;
-	dentries = __chk_dentries(sbi, child_cnt, child_files,
-			(unsigned long *)de_blk->dentry_bitmap,
+	dentries = __chk_dentries(sbi, child,
+			de_blk->dentry_bitmap,
 			de_blk->dentry, de_blk->filename,
-			NR_INLINE_DENTRY, 1);
+			NR_INLINE_DENTRY, 1,
+			file_is_encrypt(&node_blk->i));
 	if (dentries < 0) {
 		DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n",
 			fsck->dentry_depth);
@@ -820,7 +1408,7 @@
 }
 
 int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
-		u32 *child_cnt, u32 *child_files, int last_blk)
+		struct child_info *child, int last_blk, int encrypted)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	struct f2fs_dentry_block *de_blk;
@@ -833,12 +1421,12 @@
 	ASSERT(ret >= 0);
 
 	fsck->dentry_depth++;
-	dentries = __chk_dentries(sbi, child_cnt, child_files,
-			(unsigned long *)de_blk->dentry_bitmap,
+	dentries = __chk_dentries(sbi, child,
+			de_blk->dentry_bitmap,
 			de_blk->dentry, de_blk->filename,
-			NR_DENTRY_IN_BLOCK, last_blk);
+			NR_DENTRY_IN_BLOCK, last_blk, encrypted);
 
-	if (dentries < 0) {
+	if (dentries < 0 && !c.ro) {
 		ret = dev_write_block(de_blk, blk_addr);
 		ASSERT(ret >= 0);
 		DBG(1, "[%3d] Dentry Block [0x%x] Fixed hash_codes\n\n",
@@ -855,8 +1443,9 @@
 }
 
 int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
-		u32 *child_cnt, u32 *child_files, int last_blk,
-		enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver)
+		struct child_info *child, int last_blk,
+		enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver,
+		int encrypted)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 
@@ -867,7 +1456,7 @@
 	}
 
 	if (!IS_VALID_BLK_ADDR(sbi, blk_addr)) {
-		ASSERT_MSG("blkaddres is not valid. [0x%x]", blk_addr);
+		ASSERT_MSG("blkaddress is not valid. [0x%x]", blk_addr);
 		return -EINVAL;
 	}
 
@@ -885,54 +1474,195 @@
 		ASSERT_MSG("Duplicated data [0x%x]. pnid[0x%x] idx[0x%x]",
 				blk_addr, parent_nid, idx_in_node);
 
-
 	fsck->chk.valid_blk_cnt++;
 
 	if (ftype == F2FS_FT_DIR) {
 		f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_HOT_DATA);
-		return fsck_chk_dentry_blk(sbi, blk_addr, child_cnt,
-				child_files, last_blk);
+		return fsck_chk_dentry_blk(sbi, blk_addr, child,
+						last_blk, encrypted);
 	} else {
 		f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_WARM_DATA);
 	}
 	return 0;
 }
 
-void fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
+int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
 {
 	u32 blk_cnt = 0;
 	block_t start_blk, orphan_blkaddr, i, j;
-	struct f2fs_orphan_block *orphan_blk;
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_orphan_block *orphan_blk, *new_blk;
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	u32 entry_count;
 
-	if (!is_set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG))
-		return;
+	if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG))
+		return 0;
 
-	start_blk = __start_cp_addr(sbi) + 1 +
-		le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
-	orphan_blkaddr = __start_sum_addr(sbi) - 1;
+	start_blk = __start_cp_addr(sbi) + 1 + get_sb(cp_payload);
+	orphan_blkaddr = __start_sum_addr(sbi) - 1 - get_sb(cp_payload);
+
 	orphan_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(orphan_blk);
+
+	new_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(new_blk);
 
 	for (i = 0; i < orphan_blkaddr; i++) {
 		int ret = dev_read_block(orphan_blk, start_blk + i);
+		u32 new_entry_count = 0;
 
 		ASSERT(ret >= 0);
+		entry_count = le32_to_cpu(orphan_blk->entry_count);
 
-		for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
+		for (j = 0; j < entry_count; j++) {
 			nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
 			DBG(1, "[%3d] ino [0x%x]\n", i, ino);
-			if (config.fix_on) {
-				FIX_MSG("Discard orphan inodes: ino [0x%x]",
-									ino);
+			struct node_info ni;
+			blk_cnt = 1;
+
+			if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
+				get_node_info(sbi, ino, &ni);
+				if (!IS_VALID_NID(sbi, ino) ||
+						!IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+					return -EINVAL;
+
 				continue;
 			}
-			blk_cnt = 1;
-			fsck_chk_node_blk(sbi, NULL, ino,
-					F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt);
+
+			ret = fsck_chk_node_blk(sbi, NULL, ino, NULL,
+					F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt,
+					NULL);
+			if (!ret)
+				new_blk->ino[new_entry_count++] =
+							orphan_blk->ino[j];
+			else if (ret && c.fix_on)
+				FIX_MSG("[0x%x] remove from orphan list", ino);
+			else if (ret)
+				ASSERT_MSG("[0x%x] wrong orphan inode", ino);
+		}
+		if (!c.ro && c.fix_on &&
+				entry_count != new_entry_count) {
+			new_blk->entry_count = cpu_to_le32(new_entry_count);
+			ret = dev_write_block(new_blk, start_blk + i);
+			ASSERT(ret >= 0);
 		}
 		memset(orphan_blk, 0, BLOCK_SZ);
+		memset(new_blk, 0, BLOCK_SZ);
 	}
 	free(orphan_blk);
+	free(new_blk);
+
+	return 0;
+}
+
+int fsck_chk_meta(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	struct seg_entry *se;
+	unsigned int sit_valid_segs = 0, sit_node_blks = 0;
+	unsigned int i;
+
+	/* 1. check sit usage with CP: curseg is lost? */
+	for (i = 0; i < TOTAL_SEGS(sbi); i++) {
+		se = get_seg_entry(sbi, i);
+		if (se->valid_blocks != 0)
+			sit_valid_segs++;
+		else if (IS_CUR_SEGNO(sbi, i, NO_CHECK_TYPE)) {
+			/* curseg has not been written back to device */
+			MSG(1, "\tInfo: curseg %u is counted in valid segs\n", i);
+			sit_valid_segs++;
+		}
+		if (IS_NODESEG(se->type))
+			sit_node_blks += se->valid_blocks;
+	}
+	if (fsck->chk.sit_free_segs + sit_valid_segs != TOTAL_SEGS(sbi)) {
+		ASSERT_MSG("SIT usage does not match: sit_free_segs %u, "
+				"sit_valid_segs %u, total_segs %u",
+			fsck->chk.sit_free_segs, sit_valid_segs,
+			TOTAL_SEGS(sbi));
+		return -EINVAL;
+	}
+
+	/* 2. check node count */
+	if (fsck->chk.valid_nat_entry_cnt != sit_node_blks) {
+		ASSERT_MSG("node count does not match: valid_nat_entry_cnt %u,"
+			" sit_node_blks %u",
+			fsck->chk.valid_nat_entry_cnt, sit_node_blks);
+		return -EINVAL;
+	}
+
+	/* 3. check SIT with CP */
+	if (fsck->chk.sit_free_segs != le32_to_cpu(cp->free_segment_count)) {
+		ASSERT_MSG("free segs does not match: sit_free_segs %u, "
+				"free_segment_count %u",
+				fsck->chk.sit_free_segs,
+				le32_to_cpu(cp->free_segment_count));
+		return -EINVAL;
+	}
+
+	/* 4. check NAT with CP */
+	if (fsck->chk.valid_nat_entry_cnt !=
+					le32_to_cpu(cp->valid_node_count)) {
+		ASSERT_MSG("valid node does not match: valid_nat_entry_cnt %u,"
+				" valid_node_count %u",
+				fsck->chk.valid_nat_entry_cnt,
+				le32_to_cpu(cp->valid_node_count));
+		return -EINVAL;
+	}
+
+	/* 4. check orphan inode simply */
+	if (fsck_chk_orphan_node(sbi))
+		return -EINVAL;
+
+	if (fsck->nat_valid_inode_cnt != le32_to_cpu(cp->valid_inode_count)) {
+		ASSERT_MSG("valid inode does not match: nat_valid_inode_cnt %u,"
+				" valid_inode_count %u",
+				fsck->nat_valid_inode_cnt,
+				le32_to_cpu(cp->valid_inode_count));
+		return -EINVAL;
+	}
+
+	/*check nat entry with sit_area_bitmap*/
+	for (i = 0; i < fsck->nr_nat_entries; i++) {
+		u32 blk = le32_to_cpu(fsck->entries[i].block_addr);
+		nid_t ino = le32_to_cpu(fsck->entries[i].ino);
+
+		if (!blk)
+			/*
+			 * skip entry whose ino is 0, otherwise, we will
+			 * get a negative number by BLKOFF_FROM_MAIN(sbi, blk)
+			 */
+			continue;
+
+		if (!IS_VALID_BLK_ADDR(sbi, blk)) {
+			MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]"
+				" is in valid\n",
+				ino, blk);
+			return -EINVAL;
+		}
+
+		if (!f2fs_test_sit_bitmap(sbi, blk)) {
+			MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]"
+				" not find it in sit_area_bitmap\n",
+				ino, blk);
+			return -EINVAL;
+		}
+
+		if (!IS_VALID_NID(sbi, ino)) {
+			MSG(0, "\tError: nat_entry->ino %u exceeds the range"
+				" of nat entries %u\n",
+				ino, fsck->nr_nat_entries);
+			return -EINVAL;
+		}
+
+		if (!f2fs_test_bit(ino, fsck->nat_area_bitmap)) {
+			MSG(0, "\tError: nat_entry->ino %u is not set in"
+				" nat_area_bitmap\n", ino);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
 }
 
 void fsck_init(struct f2fs_sb_info *sbi)
@@ -958,10 +1688,46 @@
 
 	build_sit_area_bitmap(sbi);
 
+	ASSERT(tree_mark_size != 0);
 	tree_mark = calloc(tree_mark_size, 1);
 	ASSERT(tree_mark != NULL);
 }
 
+static void fix_hard_links(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct hard_link_node *tmp, *node;
+	struct f2fs_node *node_blk = NULL;
+	struct node_info ni;
+	int ret;
+
+	if (fsck->hard_link_list_head == NULL)
+		return;
+
+	node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+	ASSERT(node_blk != NULL);
+
+	node = fsck->hard_link_list_head;
+	while (node) {
+		/* Sanity check */
+		if (sanity_check_nid(sbi, node->nid, node_blk,
+					F2FS_FT_MAX, TYPE_INODE, &ni, NULL))
+			FIX_MSG("Failed to fix, rerun fsck.f2fs");
+
+		node_blk->i.i_links = cpu_to_le32(node->actual_links);
+
+		FIX_MSG("File: 0x%x i_links= 0x%x -> 0x%x",
+				node->nid, node->links, node->actual_links);
+
+		ret = dev_write_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+		tmp = node;
+		node = node->next;
+		free(tmp);
+	}
+	free(node_blk);
+}
+
 static void fix_nat_entries(struct f2fs_sb_info *sbi)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
@@ -972,44 +1738,73 @@
 			nullify_nat_entry(sbi, i);
 }
 
+static void flush_curseg_sit_entries(struct f2fs_sb_info *sbi)
+{
+	struct sit_info *sit_i = SIT_I(sbi);
+	int i;
+
+	/* update curseg sit entries, since we may change
+	 * a segment type in move_curseg_info
+	 */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+		struct f2fs_sit_block *sit_blk;
+		struct f2fs_sit_entry *sit;
+		struct seg_entry *se;
+
+		se = get_seg_entry(sbi, curseg->segno);
+		sit_blk = get_current_sit_page(sbi, curseg->segno);
+		sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, curseg->segno)];
+		sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+							se->valid_blocks);
+		rewrite_current_sit_page(sbi, curseg->segno, sit_blk);
+		free(sit_blk);
+	}
+}
+
 static void fix_checkpoint(struct f2fs_sb_info *sbi)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
-	struct f2fs_super_block *raw_sb = sbi->raw_super;
-	struct f2fs_checkpoint *ckp = F2FS_CKPT(sbi);
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 	unsigned long long cp_blk_no;
+	u32 flags = CP_UMOUNT_FLAG;
+	block_t orphan_blks = 0;
 	u32 i;
 	int ret;
 	u_int32_t crc = 0;
 
-	ckp->ckpt_flags = cpu_to_le32(CP_UMOUNT_FLAG);
-	ckp->cp_pack_total_block_count =
-		cpu_to_le32(8 + le32_to_cpu(raw_sb->cp_payload));
-	ckp->cp_pack_start_sum = cpu_to_le32(1 +
-				le32_to_cpu(raw_sb->cp_payload));
+	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) {
+		orphan_blks = __start_sum_addr(sbi) - 1;
+		flags |= CP_ORPHAN_PRESENT_FLAG;
+	}
 
-	ckp->free_segment_count = cpu_to_le32(fsck->chk.free_segs);
-	ckp->valid_block_count = cpu_to_le32(fsck->chk.valid_blk_cnt);
-	ckp->valid_node_count = cpu_to_le32(fsck->chk.valid_node_cnt);
-	ckp->valid_inode_count = cpu_to_le32(fsck->chk.valid_inode_cnt);
+	set_cp(ckpt_flags, flags);
+	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload));
 
-	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, ckp, CHECKSUM_OFFSET);
-	*((__le32 *)((unsigned char *)ckp + CHECKSUM_OFFSET)) =
-							cpu_to_le32(crc);
+	set_cp(free_segment_count, get_free_segments(sbi));
+	set_cp(valid_block_count, fsck->chk.valid_blk_cnt);
+	set_cp(valid_node_count, fsck->chk.valid_node_cnt);
+	set_cp(valid_inode_count, fsck->chk.valid_inode_cnt);
 
-	cp_blk_no = le32_to_cpu(raw_sb->cp_blkaddr);
+	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
+	*((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+
+	cp_blk_no = get_sb(cp_blkaddr);
 	if (sbi->cur_cp == 2)
-		cp_blk_no += 1 << le32_to_cpu(raw_sb->log_blocks_per_seg);
+		cp_blk_no += 1 << get_sb(log_blocks_per_seg);
 
-	ret = dev_write_block(ckp, cp_blk_no++);
+	ret = dev_write_block(cp, cp_blk_no++);
 	ASSERT(ret >= 0);
 
-	for (i = 0; i < le32_to_cpu(raw_sb->cp_payload); i++) {
-		ret = dev_write_block(((unsigned char *)ckp) + i * F2FS_BLKSIZE,
+	for (i = 0; i < get_sb(cp_payload); i++) {
+		ret = dev_write_block(((unsigned char *)cp) + i * F2FS_BLKSIZE,
 								cp_blk_no++);
 		ASSERT(ret >= 0);
 	}
 
+	cp_blk_no += orphan_blks;
+
 	for (i = 0; i < NO_CHECK_TYPE; i++) {
 		struct curseg_info *curseg = CURSEG_I(sbi, i);
 
@@ -1017,7 +1812,7 @@
 		ASSERT(ret >= 0);
 	}
 
-	ret = dev_write_block(ckp, cp_blk_no++);
+	ret = dev_write_block(cp, cp_blk_no++);
 	ASSERT(ret >= 0);
 }
 
@@ -1028,13 +1823,26 @@
 	for (i = 0; i < NO_CHECK_TYPE; i++) {
 		struct curseg_info *curseg = CURSEG_I(sbi, i);
 		struct seg_entry *se;
+		int j, nblocks;
 
+		if ((curseg->next_blkoff >> 3) >= SIT_VBLOCK_MAP_SIZE)
+			return -EINVAL;
 		se = get_seg_entry(sbi, curseg->segno);
 		if (f2fs_test_bit(curseg->next_blkoff,
-				(const char *)se->cur_valid_map) == 1) {
+					(const char *)se->cur_valid_map)) {
 			ASSERT_MSG("Next block offset is not free, type:%d", i);
 			return -EINVAL;
 		}
+		if (curseg->alloc_type == SSR)
+			return 0;
+
+		nblocks = sbi->blocks_per_seg;
+		for (j = curseg->next_blkoff + 1; j < nblocks; j++) {
+			if (f2fs_test_bit(j, (const char *)se->cur_valid_map)) {
+				ASSERT_MSG("LFS must have free section:%d", i);
+				return -EINVAL;
+			}
+		}
 	}
 	return 0;
 }
@@ -1049,7 +1857,8 @@
 
 		se = get_seg_entry(sbi, i);
 		if (se->orig_type != se->type) {
-			if (se->orig_type == CURSEG_COLD_DATA) {
+			if (se->orig_type == CURSEG_COLD_DATA &&
+					se->type <= CURSEG_COLD_DATA) {
 				se->type = se->orig_type;
 			} else {
 				FIX_MSG("Wrong segment type [0x%x] %x -> %x",
@@ -1086,7 +1895,7 @@
 					node->nid, node->links);
 			node = node->next;
 		}
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] Unreachable nat entries                       ");
@@ -1095,7 +1904,7 @@
 	} else {
 		printf(" [Fail] [0x%x]\n", nr_unref_nid);
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] SIT valid block bitmap checking                ");
@@ -1105,7 +1914,7 @@
 	} else {
 		printf("[Fail]\n");
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] Hard link checking for regular file           ");
@@ -1114,7 +1923,7 @@
 	} else {
 		printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files);
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] valid_block_count matching with CP            ");
@@ -1123,7 +1932,7 @@
 	} else {
 		printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt);
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] valid_node_count matcing with CP (de lookup)  ");
@@ -1132,7 +1941,7 @@
 	} else {
 		printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt);
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] valid_node_count matcing with CP (nat lookup) ");
@@ -1141,7 +1950,7 @@
 	} else {
 		printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt);
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] valid_inode_count matched with CP             ");
@@ -1150,7 +1959,7 @@
 	} else {
 		printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt);
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] free segment_count matched with CP            ");
@@ -1160,7 +1969,7 @@
 	} else {
 		printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs);
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] next block offset is free                     ");
@@ -1169,7 +1978,7 @@
 	} else {
 		printf(" [Fail]\n");
 		ret = EXIT_ERR_CODE;
-		config.bug_on = 1;
+		c.bug_on = 1;
 	}
 
 	printf("[FSCK] fixing SIT types\n");
@@ -1177,18 +1986,43 @@
 		force = 1;
 
 	printf("[FSCK] other corrupted bugs                          ");
-	if (config.bug_on == 0) {
+	if (c.bug_on == 0) {
 		printf(" [Ok..]\n");
 	} else {
 		printf(" [Fail]\n");
 		ret = EXIT_ERR_CODE;
 	}
 
+#ifndef WITH_ANDROID
+	if (nr_unref_nid && !c.ro) {
+		char ans[255] = {0};
+
+		printf("\nDo you want to restore lost files into ./lost_found/? [Y/N] ");
+		ret = scanf("%s", ans);
+		ASSERT(ret >= 0);
+		if (!strcasecmp(ans, "y")) {
+			for (i = 0; i < fsck->nr_nat_entries; i++) {
+				if (f2fs_test_bit(i, fsck->nat_area_bitmap))
+					dump_node(sbi, i, 1);
+			}
+		}
+	}
+#endif
 	/* fix global metadata */
-	if (force || (config.bug_on && config.fix_on)) {
-		fix_nat_entries(sbi);
-		rewrite_sit_area_bitmap(sbi);
-		fix_checkpoint(sbi);
+	if (force || (c.fix_on && !c.ro)) {
+		struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+
+		if (force || c.bug_on) {
+			fix_hard_links(sbi);
+			fix_nat_entries(sbi);
+			rewrite_sit_area_bitmap(sbi);
+			move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
+			write_curseg_info(sbi);
+			flush_curseg_sit_entries(sbi);
+			fix_checkpoint(sbi);
+		} else if (is_set_ckpt_flags(cp, CP_FSCK_FLAG)) {
+			write_checkpoint(sbi);
+		}
 	}
 	return ret;
 }
@@ -1205,6 +2039,9 @@
 	if (fsck->sit_area_bitmap)
 		free(fsck->sit_area_bitmap);
 
+	if (fsck->entries)
+		free(fsck->entries);
+
 	if (tree_mark)
 		free(tree_mark);
 }
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 9cad013..5a6f018 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -13,12 +13,47 @@
 
 #include "f2fs.h"
 
+#define FSCK_UNMATCHED_EXTENT		0x00000001
+
+enum {
+	PREEN_MODE_0,
+	PREEN_MODE_1,
+	PREEN_MODE_MAX
+};
+
+enum {
+	NOERROR,
+	EWRONG_OPT,
+	ENEED_ARG,
+	EUNKNOWN_OPT,
+	EUNKNOWN_ARG,
+};
+
 /* fsck.c */
 struct orphan_info {
 	u32 nr_inodes;
 	u32 *ino_list;
 };
 
+struct extent_info {
+	u32 fofs;		/* start offset in a file */
+	u32 blk;		/* start block address of the extent */
+	u32 len;		/* length of the extent */
+};
+
+struct child_info {
+	u32 state;
+	u32 links;
+	u32 files;
+	u32 pgofs;
+	u8 dots;
+	u8 dir_level;
+	u32 p_ino;		/*parent ino*/
+	u32 pp_ino;		/*parent parent ino*/
+	struct extent_info ei;
+	u32 last_blk;
+};
+
 struct f2fs_fsck {
 	struct f2fs_sb_info sbi;
 
@@ -31,7 +66,6 @@
 		u32 multi_hard_link_files;
 		u64 sit_valid_blocks;
 		u32 sit_free_segs;
-		u32 free_segs;
 	} chk;
 
 	struct hard_link_node *hard_link_list_head;
@@ -49,6 +83,8 @@
 	u32 nr_nat_entries;
 
 	u32 dentry_depth;
+	struct f2fs_nat_entry *entries;
+	u32 nat_valid_inode_cnt;
 };
 
 #define BLOCK_SZ		4096
@@ -67,6 +103,7 @@
 struct hard_link_node {
 	u32 nid;
 	u32 links;
+	u32 actual_links;
 	struct hard_link_node *next;
 };
 
@@ -78,30 +115,40 @@
 	SEG_TYPE_MAX,
 };
 
-extern void fsck_chk_orphan_node(struct f2fs_sb_info *);
+struct selabel_handle;
+
+extern int fsck_chk_orphan_node(struct f2fs_sb_info *);
 extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
-		enum FILE_TYPE, enum NODE_TYPE, u32 *);
+		u8 *, enum FILE_TYPE, enum NODE_TYPE, u32 *,
+		struct child_info *);
 extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE,
 		struct f2fs_node *, u32 *, struct node_info *);
 extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
 		u32, enum FILE_TYPE, struct f2fs_node *, u32 *,
-		struct node_info *);
+		struct child_info *, struct node_info *);
 extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
-		enum FILE_TYPE, struct f2fs_node *, u32 *);
+		enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
 extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
-		enum FILE_TYPE, struct f2fs_node *, u32 *);
-extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, u32 *, u32 *,
-		int, enum FILE_TYPE, u32, u16, u8);
-extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, u32 *, u32 *, int);
+		enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
+extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, struct child_info *,
+		int, enum FILE_TYPE, u32, u16, u8, int);
+extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, struct child_info *,
+		int, int);
 int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *,
-		u32 *, u32 *);
+		struct child_info *);
+int fsck_chk_meta(struct f2fs_sb_info *sbi);
+int convert_encrypted_name(unsigned char *, int, unsigned char *, int);
 
-extern void print_node_info(struct f2fs_node *);
+extern void update_free_segments(struct f2fs_sb_info *);
+void print_cp_state(u32);
+extern void print_node_info(struct f2fs_node *, int);
 extern void print_inode_info(struct f2fs_inode *, int);
 extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int);
-extern int get_sum_block(struct f2fs_sb_info *, unsigned int,
-				struct f2fs_summary_block *);
+extern struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *,
+				unsigned int, int *);
 extern int get_sum_entry(struct f2fs_sb_info *, u32, struct f2fs_summary *);
+extern void update_sum_entry(struct f2fs_sb_info *, block_t,
+				struct f2fs_summary *);
 extern void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
 extern void nullify_nat_entry(struct f2fs_sb_info *, u32);
 extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *);
@@ -113,9 +160,29 @@
 extern int f2fs_do_mount(struct f2fs_sb_info *);
 extern void f2fs_do_umount(struct f2fs_sb_info *);
 
+extern void flush_journal_entries(struct f2fs_sb_info *);
+extern void zero_journal_entries(struct f2fs_sb_info *);
+extern void flush_sit_entries(struct f2fs_sb_info *);
+extern void move_curseg_info(struct f2fs_sb_info *, u64);
+extern void write_curseg_info(struct f2fs_sb_info *);
+extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int);
+extern void write_checkpoint(struct f2fs_sb_info *);
+extern void update_data_blkaddr(struct f2fs_sb_info *, nid_t, u16, block_t);
+extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, nid_t, block_t);
+
+extern void print_raw_sb_info(struct f2fs_super_block *);
+
+extern u32 get_free_segments(struct f2fs_sb_info *);
+extern struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *,
+			unsigned int);
+extern void rewrite_current_sit_page(struct f2fs_sb_info *, unsigned int,
+			struct f2fs_sit_block *);
+
 /* dump.c */
 struct dump_option {
 	nid_t nid;
+	int start_nat;
+	int end_nat;
 	int start_sit;
 	int end_sit;
 	int start_ssa;
@@ -123,9 +190,36 @@
 	int32_t blk_addr;
 };
 
-extern void sit_dump(struct f2fs_sb_info *, int, int);
+extern void nat_dump(struct f2fs_sb_info *);
+extern void sit_dump(struct f2fs_sb_info *, unsigned int, unsigned int);
 extern void ssa_dump(struct f2fs_sb_info *, int, int);
-extern void dump_node(struct f2fs_sb_info *, nid_t);
+extern void dump_node(struct f2fs_sb_info *, nid_t, int);
 extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
 
+/* defrag.c */
+int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int);
+
+/* resize.c */
+int f2fs_resize(struct f2fs_sb_info *);
+
+/* sload.c */
+int f2fs_sload(struct f2fs_sb_info *, const char *, const char *,
+		const char *, struct selabel_handle *);
+void reserve_new_block(struct f2fs_sb_info *, block_t *,
+					struct f2fs_summary *, int);
+void new_data_block(struct f2fs_sb_info *, void *,
+					struct dnode_of_data *, int);
+int f2fs_build_file(struct f2fs_sb_info *, struct dentry *);
+void f2fs_alloc_nid(struct f2fs_sb_info *, nid_t *, int);
+void set_data_blkaddr(struct dnode_of_data *);
+block_t new_node_block(struct f2fs_sb_info *,
+					struct dnode_of_data *, unsigned int);
+void get_dnode_of_data(struct f2fs_sb_info *, struct dnode_of_data *,
+					pgoff_t, int);
+int f2fs_create(struct f2fs_sb_info *, struct dentry *);
+int f2fs_mkdir(struct f2fs_sb_info *, struct dentry *);
+int f2fs_symlink(struct f2fs_sb_info *, struct dentry *);
+int inode_set_selinux(struct f2fs_sb_info *, u32, const char *);
+int f2fs_find_path(struct f2fs_sb_info *, char *, nid_t *);
+
 #endif /* _FSCK_H_ */
diff --git a/fsck/main.c b/fsck/main.c
index 3606f62..6c94a70 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -3,6 +3,13 @@
  *
  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com/
+ * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+ *  : implement defrag.f2fs
+ * Copyright (C) 2015 Huawei Ltd.
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *  : add sload.f2fs
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -10,6 +17,7 @@
  */
 #include "fsck.h"
 #include <libgen.h>
+#include <ctype.h>
 
 struct f2fs_fsck gfsck;
 
@@ -20,6 +28,7 @@
 	MSG(0, "  -a check/fix potential corruption, reported by f2fs\n");
 	MSG(0, "  -d debug level [default:0]\n");
 	MSG(0, "  -f check/fix entire partition\n");
+	MSG(0, "  -p preen mode [default:0 the same as -a [0|1]]\n");
 	MSG(0, "  -t show directory tree [-d -1]\n");
 	exit(1);
 }
@@ -30,6 +39,7 @@
 	MSG(0, "[options]:\n");
 	MSG(0, "  -d debug level [default:0]\n");
 	MSG(0, "  -i inode no (hex)\n");
+	MSG(0, "  -n [NAT dump segno from #1~#2 (decimal), for all 0~-1]\n");
 	MSG(0, "  -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
 	MSG(0, "  -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
 	MSG(0, "  -b blk_addr (in 4KB)\n");
@@ -37,43 +47,153 @@
 	exit(1);
 }
 
+void defrag_usage()
+{
+	MSG(0, "\nUsage: defrag.f2fs [options] device\n");
+	MSG(0, "[options]:\n");
+	MSG(0, "  -d debug level [default:0]\n");
+	MSG(0, "  -s start block address [default: main_blkaddr]\n");
+	MSG(0, "  -l length [default:512 (2MB)]\n");
+	MSG(0, "  -t target block address [default: main_blkaddr + 2MB]\n");
+	MSG(0, "  -i set direction as shrink [default: expand]\n");
+	exit(1);
+}
+
+void resize_usage()
+{
+	MSG(0, "\nUsage: resize.f2fs [options] device\n");
+	MSG(0, "[options]:\n");
+	MSG(0, "  -d debug level [default:0]\n");
+	MSG(0, "  -t target sectors [default: device size]\n");
+	exit(1);
+}
+
+void sload_usage()
+{
+	MSG(0, "\nUsage: sload.f2fs [options] device\n");
+	MSG(0, "[options]:\n");
+	MSG(0, "  -f source directory [path of the source directory]\n");
+	MSG(0, "  -t mount point [prefix of target fs path, default:/]\n");
+	MSG(0, "  -d debug level [default:0]\n");
+	exit(1);
+}
+
+static int is_digits(char *optarg)
+{
+	int i;
+
+	for (i = 0; i < strlen(optarg); i++)
+		if (!isdigit(optarg[i]))
+			break;
+	return i == strlen(optarg);
+}
+
+static void error_out(void)
+{
+	if (c.func == FSCK)
+		fsck_usage();
+	else if (c.func == DUMP)
+		dump_usage();
+	else if (c.func == DEFRAG)
+		defrag_usage();
+	else if (c.func == RESIZE)
+		resize_usage();
+	else if (c.func == SLOAD)
+		sload_usage();
+}
+
 void f2fs_parse_options(int argc, char *argv[])
 {
 	int option = 0;
 	char *prog = basename(argv[0]);
+	int err = NOERROR;
+
+	if (argc < 2) {
+		MSG(0, "\tError: Device not specified\n");
+		error_out();
+	}
+	c.devices[0].path = strdup(argv[argc - 1]);
+	argv[argc-- - 1] = 0;
 
 	if (!strcmp("fsck.f2fs", prog)) {
-		const char *option_string = "ad:ft";
+		const char *option_string = ":ad:fp:t";
 
-		config.func = FSCK;
+		c.func = FSCK;
 		while ((option = getopt(argc, argv, option_string)) != EOF) {
 			switch (option) {
 			case 'a':
-				config.auto_fix = 1;
+				c.auto_fix = 1;
 				MSG(0, "Info: Fix the reported corruption.\n");
 				break;
+			case 'p':
+				/* preen mode has different levels:
+				 *  0: default level, the same as -a
+				 *  1: check meta
+				 */
+				if (optarg[0] == '-') {
+					c.preen_mode = PREEN_MODE_0;
+					optind--;
+					break;
+				} else if (!is_digits(optarg)) {
+					err = EWRONG_OPT;
+					break;
+				}
+				c.preen_mode = atoi(optarg);
+				if (c.preen_mode < 0)
+					c.preen_mode = PREEN_MODE_0;
+				else if (c.preen_mode >= PREEN_MODE_MAX)
+					c.preen_mode = PREEN_MODE_MAX - 1;
+				if (c.preen_mode == PREEN_MODE_0)
+					c.auto_fix = 1;
+				MSG(0, "Info: Fix the reported corruption in "
+					"preen mode %d\n", c.preen_mode);
+				break;
 			case 'd':
-				config.dbg_lv = atoi(optarg);
-				MSG(0, "Info: Debug level = %d\n",
-							config.dbg_lv);
+				if (optarg[0] == '-') {
+					err = ENEED_ARG;
+					break;
+				} else if (!is_digits(optarg)) {
+					err = EWRONG_OPT;
+					break;
+				}
+				c.dbg_lv = atoi(optarg);
+				MSG(0, "Info: Debug level = %d\n", c.dbg_lv);
 				break;
 			case 'f':
-				config.fix_on = 1;
+				c.fix_on = 1;
 				MSG(0, "Info: Force to fix corruption\n");
 				break;
 			case 't':
-				config.dbg_lv = -1;
+				c.dbg_lv = -1;
 				break;
+
+
+			case ':':
+				if (optopt == 'p') {
+					MSG(0, "Info: Use default preen mode\n");
+					c.preen_mode = PREEN_MODE_0;
+					c.auto_fix = 1;
+				} else {
+					option = optopt;
+					err = ENEED_ARG;
+					break;
+				}
+				break;
+			case '?':
+				option = optopt;
 			default:
-				MSG(0, "\tError: Unknown option %c\n", option);
-				fsck_usage();
+				err = EUNKNOWN_OPT;
 				break;
 			}
+			if (err != NOERROR)
+				break;
 		}
 	} else if (!strcmp("dump.f2fs", prog)) {
-		const char *option_string = "d:i:s:a:b:";
+		const char *option_string = "d:i:n:s:a:b:";
 		static struct dump_option dump_opt = {
-			.nid = 3,	/* default root ino */
+			.nid = 0,	/* default root ino */
+			.start_nat = -1,
+			.end_nat = -1,
 			.start_sit = -1,
 			.end_sit = -1,
 			.start_ssa = -1,
@@ -81,15 +201,19 @@
 			.blk_addr = -1,
 		};
 
-		config.func = DUMP;
+		c.func = DUMP;
 		while ((option = getopt(argc, argv, option_string)) != EOF) {
 			int ret = 0;
 
 			switch (option) {
 			case 'd':
-				config.dbg_lv = atoi(optarg);
+				if (!is_digits(optarg)) {
+					err = EWRONG_OPT;
+					break;
+				}
+				c.dbg_lv = atoi(optarg);
 				MSG(0, "Info: Debug level = %d\n",
-							config.dbg_lv);
+							c.dbg_lv);
 				break;
 			case 'i':
 				if (strncmp(optarg, "0x", 2))
@@ -99,6 +223,11 @@
 					ret = sscanf(optarg, "%x",
 							&dump_opt.nid);
 				break;
+			case 'n':
+				ret = sscanf(optarg, "%d~%d",
+							&dump_opt.start_nat,
+							&dump_opt.end_nat);
+				break;
 			case 's':
 				ret = sscanf(optarg, "%d~%d",
 							&dump_opt.start_sit,
@@ -118,83 +247,313 @@
 							&dump_opt.blk_addr);
 				break;
 			default:
-				MSG(0, "\tError: Unknown option %c\n", option);
-				dump_usage();
+				err = EUNKNOWN_OPT;
 				break;
 			}
 			ASSERT(ret >= 0);
+			if (err != NOERROR)
+				break;
 		}
 
-		config.private = &dump_opt;
-	}
+		c.private = &dump_opt;
+	} else if (!strcmp("defrag.f2fs", prog)) {
+		const char *option_string = "d:s:l:t:i";
 
-	if ((optind + 1) != argc) {
-		MSG(0, "\tError: Device not specified\n");
-		if (config.func == FSCK)
-			fsck_usage();
-		else if (config.func == DUMP)
-			dump_usage();
+		c.func = DEFRAG;
+		while ((option = getopt(argc, argv, option_string)) != EOF) {
+			int ret = 0;
+
+			switch (option) {
+			case 'd':
+				if (!is_digits(optarg)) {
+					err = EWRONG_OPT;
+					break;
+				}
+				c.dbg_lv = atoi(optarg);
+				MSG(0, "Info: Debug level = %d\n",
+							c.dbg_lv);
+				break;
+			case 's':
+				if (strncmp(optarg, "0x", 2))
+					ret = sscanf(optarg, "%"PRIu64"",
+							&c.defrag_start);
+				else
+					ret = sscanf(optarg, "%"PRIx64"",
+							&c.defrag_start);
+				break;
+			case 'l':
+				if (strncmp(optarg, "0x", 2))
+					ret = sscanf(optarg, "%"PRIu64"",
+							&c.defrag_len);
+				else
+					ret = sscanf(optarg, "%"PRIx64"",
+							&c.defrag_len);
+				break;
+			case 't':
+				if (strncmp(optarg, "0x", 2))
+					ret = sscanf(optarg, "%"PRIu64"",
+							&c.defrag_target);
+				else
+					ret = sscanf(optarg, "%"PRIx64"",
+							&c.defrag_target);
+				break;
+			case 'i':
+				c.defrag_shrink = 1;
+				break;
+			default:
+				err = EUNKNOWN_OPT;
+				break;
+			}
+			ASSERT(ret >= 0);
+			if (err != NOERROR)
+				break;
+		}
+	} else if (!strcmp("resize.f2fs", prog)) {
+		const char *option_string = "d:t:";
+
+		c.func = RESIZE;
+		while ((option = getopt(argc, argv, option_string)) != EOF) {
+			int ret = 0;
+
+			switch (option) {
+			case 'd':
+				if (!is_digits(optarg)) {
+					err = EWRONG_OPT;
+					break;
+				}
+				c.dbg_lv = atoi(optarg);
+				MSG(0, "Info: Debug level = %d\n",
+							c.dbg_lv);
+				break;
+			case 't':
+				if (strncmp(optarg, "0x", 2))
+					ret = sscanf(optarg, "%"PRIu64"",
+							&c.target_sectors);
+				else
+					ret = sscanf(optarg, "%"PRIx64"",
+							&c.target_sectors);
+				break;
+			default:
+				err = EUNKNOWN_OPT;
+				break;
+			}
+			ASSERT(ret >= 0);
+			if (err != NOERROR)
+				break;
+		}
+	} else if (!strcmp("sload.f2fs", prog)) {
+		const char *option_string = "d:f:t:";
+
+		c.func = SLOAD;
+		while ((option = getopt(argc, argv, option_string)) != EOF) {
+			switch (option) {
+			case 'd':
+				if (!is_digits(optarg)) {
+					err = EWRONG_OPT;
+					break;
+				}
+				c.dbg_lv = atoi(optarg);
+				MSG(0, "Info: Debug level = %d\n",
+						c.dbg_lv);
+				break;
+			case 'f':
+				c.from_dir = (char *)optarg;
+				break;
+			case 't':
+				c.mount_point = (char *)optarg;
+				break;
+			default:
+				err = EUNKNOWN_OPT;
+				break;
+			}
+			if (err != NOERROR)
+				break;
+		}
 	}
-	config.device_name = argv[optind];
+	if (argc > optind) {
+		c.dbg_lv = 0;
+		err = EUNKNOWN_ARG;
+	}
+	if (err == NOERROR)
+		return;
+
+	/* print out error */
+	switch (err) {
+	case EWRONG_OPT:
+		MSG(0, "\tError: Wrong option -%c %s\n", option, optarg);
+		break;
+	case ENEED_ARG:
+		MSG(0, "\tError: Need argument for -%c\n", option);
+		break;
+	case EUNKNOWN_OPT:
+		MSG(0, "\tError: Unknown option %c\n", option);
+		break;
+	case EUNKNOWN_ARG:
+		MSG(0, "\tError: Unknown argument %s\n", argv[optind]);
+		break;
+	}
+	error_out();
 }
 
 static void do_fsck(struct f2fs_sb_info *sbi)
 {
+	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	u32 flag = le32_to_cpu(ckpt->ckpt_flags);
 	u32 blk_cnt;
 
 	fsck_init(sbi);
 
+	print_cp_state(flag);
+
+	if (!c.fix_on && !c.bug_on) {
+		switch (c.preen_mode) {
+		case PREEN_MODE_1:
+			if (fsck_chk_meta(sbi)) {
+				MSG(0, "[FSCK] F2FS metadata   [Fail]");
+				MSG(0, "\tError: meta does not match, "
+					"force check all\n");
+			} else {
+				MSG(0, "[FSCK] F2FS metadata   [Ok..]");
+				fsck_free(sbi);
+				return;
+			}
+
+			if (!c.ro)
+				c.fix_on = 1;
+			break;
+		}
+	} else {
+		/*
+		 * we can hit this in 3 situations:
+		 *  1. fsck -f, fix_on has already been set to 1 when
+		 *     parsing options;
+		 *  2. fsck -a && CP_FSCK_FLAG is set, fix_on has already
+		 *     been set to 1 when checking CP_FSCK_FLAG;
+		 *  3. fsck -p 1 && error is detected, then bug_on is set,
+		 *     we set fix_on = 1 here, so that fsck can fix errors
+		 *     automatically
+		*/
+		c.fix_on = 1;
+	}
+
 	fsck_chk_orphan_node(sbi);
 
 	/* Traverse all block recursively from root inode */
 	blk_cnt = 1;
-	fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
-			F2FS_FT_DIR, TYPE_INODE, &blk_cnt);
+	fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, (u8 *)"/",
+			F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
 	fsck_verify(sbi);
 	fsck_free(sbi);
 }
 
 static void do_dump(struct f2fs_sb_info *sbi)
 {
-	struct dump_option *opt = (struct dump_option *)config.private;
+	struct dump_option *opt = (struct dump_option *)c.private;
 	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
 	u32 flag = le32_to_cpu(ckpt->ckpt_flags);
 
-	fsck_init(sbi);
-
+	if (opt->end_nat == -1)
+		opt->end_nat = NM_I(sbi)->max_nid;
 	if (opt->end_sit == -1)
 		opt->end_sit = SM_I(sbi)->main_segments;
 	if (opt->end_ssa == -1)
 		opt->end_ssa = SM_I(sbi)->main_segments;
+	if (opt->start_nat != -1)
+		nat_dump(sbi);
 	if (opt->start_sit != -1)
 		sit_dump(sbi, opt->start_sit, opt->end_sit);
 	if (opt->start_ssa != -1)
 		ssa_dump(sbi, opt->start_ssa, opt->end_ssa);
-	if (opt->blk_addr != -1) {
+	if (opt->blk_addr != -1)
 		dump_info_from_blkaddr(sbi, opt->blk_addr);
-		goto cleanup;
+	if (opt->nid)
+		dump_node(sbi, opt->nid, 0);
+
+	print_cp_state(flag);
+
+}
+
+static int do_defrag(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+
+	if (c.defrag_start > get_sb(block_count))
+		goto out_range;
+	if (c.defrag_start < SM_I(sbi)->main_blkaddr)
+		c.defrag_start = SM_I(sbi)->main_blkaddr;
+
+	if (c.defrag_len == 0)
+		c.defrag_len = sbi->blocks_per_seg;
+
+	if (c.defrag_start + c.defrag_len > get_sb(block_count))
+		c.defrag_len = get_sb(block_count) - c.defrag_start;
+
+	if (c.defrag_target == 0) {
+		c.defrag_target = c.defrag_start - 1;
+		if (!c.defrag_shrink)
+			c.defrag_target += c.defrag_len + 1;
 	}
 
-	MSG(0, "Info: checkpoint state = %x : ", flag);
-	if (flag & CP_FSCK_FLAG)
-		MSG(0, "%s", " fsck");
-	if (flag & CP_ERROR_FLAG)
-		MSG(0, "%s", " error");
-	if (flag & CP_COMPACT_SUM_FLAG)
-		MSG(0, "%s", " compacted_summary");
-	if (flag & CP_ORPHAN_PRESENT_FLAG)
-		MSG(0, "%s", " orphan_inodes");
-	if (flag & CP_FASTBOOT_FLAG)
-		MSG(0, "%s", " fastboot");
-	if (flag & CP_UMOUNT_FLAG)
-		MSG(0, "%s", " unmount");
-	else
-		MSG(0, "%s", " sudden-power-off");
-	MSG(0, "\n");
+	if (c.defrag_target < SM_I(sbi)->main_blkaddr ||
+			c.defrag_target > get_sb(block_count))
+		goto out_range;
+	if (c.defrag_target >= c.defrag_start &&
+		c.defrag_target < c.defrag_start + c.defrag_len)
+		goto out_range;
 
-	dump_node(sbi, opt->nid);
-cleanup:
-	fsck_free(sbi);
+	if (c.defrag_start > c.defrag_target)
+		MSG(0, "Info: Move 0x%"PRIx64" <- [0x%"PRIx64"-0x%"PRIx64"]\n",
+				c.defrag_target,
+				c.defrag_start,
+				c.defrag_start + c.defrag_len - 1);
+	else
+		MSG(0, "Info: Move [0x%"PRIx64"-0x%"PRIx64"] -> 0x%"PRIx64"\n",
+				c.defrag_start,
+				c.defrag_start + c.defrag_len - 1,
+				c.defrag_target);
+
+	return f2fs_defragment(sbi, c.defrag_start, c.defrag_len,
+			c.defrag_target, c.defrag_shrink);
+out_range:
+	ASSERT_MSG("Out-of-range [0x%"PRIx64" ~ 0x%"PRIx64"] to 0x%"PRIx64"",
+				c.defrag_start,
+				c.defrag_start + c.defrag_len - 1,
+				c.defrag_target);
+	return -1;
+}
+
+static int do_resize(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+
+	if (!c.target_sectors)
+		c.target_sectors = c.total_sectors;
+
+	if (c.target_sectors > c.total_sectors) {
+		ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"",
+				c.target_sectors, c.total_sectors);
+		return -1;
+	}
+
+	if (c.target_sectors <=
+			(get_sb(block_count) << get_sb(log_sectors_per_block))) {
+		ASSERT_MSG("Nothing to resize, now only support resize to expand\n");
+		return -1;
+	}
+	return f2fs_resize(sbi);
+}
+
+static int do_sload(struct f2fs_sb_info *sbi)
+{
+	if (!c.from_dir) {
+		MSG(0, "\tError: Need source directory\n");
+		sload_usage();
+		return -1;
+	}
+	if (!c.mount_point)
+		c.mount_point = "/";
+
+	return f2fs_sload(sbi, c.from_dir, c.mount_point, NULL, NULL);
 }
 
 int main(int argc, char **argv)
@@ -202,15 +561,24 @@
 	struct f2fs_sb_info *sbi;
 	int ret = 0;
 
-	f2fs_init_configuration(&config);
+	f2fs_init_configuration();
 
 	f2fs_parse_options(argc, argv);
 
-	if (f2fs_dev_is_umounted(&config) < 0)
-		return -1;
+	if (f2fs_devs_are_umounted() < 0) {
+		if (!c.ro || c.func == DEFRAG) {
+			MSG(0, "\tError: Not available on mounted device!\n");
+			return -1;
+		}
+
+		/* allow ro-mounted partition */
+		MSG(0, "Info: Check FS only due to RO\n");
+		c.fix_on = 0;
+		c.auto_fix = 0;
+	}
 
 	/* Get device */
-	if (f2fs_get_device_info(&config) < 0)
+	if (f2fs_get_device_info() < 0)
 		return -1;
 fsck_again:
 	memset(&gfsck, 0, sizeof(gfsck));
@@ -218,44 +586,66 @@
 	sbi = &gfsck.sbi;
 
 	ret = f2fs_do_mount(sbi);
-	if (ret == 1) {
-		free(sbi->ckpt);
-		free(sbi->raw_super);
-		goto out;
-	} else if (ret < 0)
-		return -1;
+	if (ret != 0) {
+		if (ret == 1) {
+			MSG(0, "Info: No error was reported\n");
+			ret = 0;
+		}
+		goto out_err;
+	}
 
-	switch (config.func) {
+	switch (c.func) {
 	case FSCK:
 		do_fsck(sbi);
 		break;
 	case DUMP:
 		do_dump(sbi);
 		break;
+#ifndef WITH_ANDROID
+	case DEFRAG:
+		ret = do_defrag(sbi);
+		if (ret)
+			goto out_err;
+		break;
+	case RESIZE:
+		if (do_resize(sbi))
+			goto out_err;
+		break;
+	case SLOAD:
+		do_sload(sbi);
+		break;
+#endif
 	}
 
 	f2fs_do_umount(sbi);
-out:
-	if (config.func == FSCK && config.bug_on) {
-		if (config.fix_on == 0 && config.auto_fix == 0) {
+
+	if (c.func == FSCK && c.bug_on) {
+		if (!c.ro && c.fix_on == 0 && c.auto_fix == 0) {
 			char ans[255] = {0};
 retry:
 			printf("Do you want to fix this partition? [Y/N] ");
 			ret = scanf("%s", ans);
 			ASSERT(ret >= 0);
 			if (!strcasecmp(ans, "y"))
-				config.fix_on = 1;
+				c.fix_on = 1;
 			else if (!strcasecmp(ans, "n"))
-				config.fix_on = 0;
+				c.fix_on = 0;
 			else
 				goto retry;
 
-			if (config.fix_on)
+			if (c.fix_on)
 				goto fsck_again;
 		}
 	}
-	f2fs_finalize_device(&config);
+	f2fs_finalize_device();
 
 	printf("\nDone.\n");
 	return 0;
+
+out_err:
+	if (sbi->ckpt)
+		free(sbi->ckpt);
+	if (sbi->raw_super)
+		free(sbi->raw_super);
+	return ret;
 }
diff --git a/fsck/mount.c b/fsck/mount.c
index 6d96db8..b8e8c45 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -11,14 +11,43 @@
 #include "fsck.h"
 #include <locale.h>
 
+u32 get_free_segments(struct f2fs_sb_info *sbi)
+{
+	u32 i, free_segs = 0;
+
+	for (i = 0; i < TOTAL_SEGS(sbi); i++) {
+		struct seg_entry *se = get_seg_entry(sbi, i);
+
+		if (se->valid_blocks == 0x0 &&
+				!IS_CUR_SEGNO(sbi, i, NO_CHECK_TYPE))
+			free_segs++;
+	}
+	return free_segs;
+}
+
+void update_free_segments(struct f2fs_sb_info *sbi)
+{
+	char *progress = "-*|*-";
+	static int i = 0;
+
+	MSG(0, "\r [ %c ] Free segments: 0x%x", progress[i % 5], get_free_segments(sbi));
+	fflush(stdout);
+	i++;
+}
+
 void print_inode_info(struct f2fs_inode *inode, int name)
 {
+	unsigned char en[F2FS_NAME_LEN + 1];
 	unsigned int i = 0;
 	int namelen = le32_to_cpu(inode->i_namelen);
+	int is_encrypt = file_is_encrypt(inode);
 
+	namelen = convert_encrypted_name(inode->i_name, namelen, en, is_encrypt);
+	en[namelen] = '\0';
 	if (name && namelen) {
 		inode->i_name[namelen] = '\0';
-		MSG(0, " - File name         : %s\n", inode->i_name);
+		MSG(0, " - File name         : %s%s\n", en,
+				is_encrypt ? " <encrypted>" : "");
 		setlocale(LC_ALL, "");
 		MSG(0, " - File size         : %'llu (bytes)\n",
 				le64_to_cpu(inode->i_size));
@@ -26,6 +55,7 @@
 	}
 
 	DISP_u32(inode, i_mode);
+	DISP_u32(inode, i_advise);
 	DISP_u32(inode, i_uid);
 	DISP_u32(inode, i_gid);
 	DISP_u32(inode, i_links);
@@ -45,17 +75,17 @@
 	DISP_u32(inode, i_flags);
 	DISP_u32(inode, i_inline);
 	DISP_u32(inode, i_pino);
+	DISP_u32(inode, i_dir_level);
 
 	if (namelen) {
 		DISP_u32(inode, i_namelen);
-		inode->i_name[namelen] = '\0';
-		DISP_utf(inode, i_name);
+		printf("%-30s\t\t[%s]\n", "i_name", en);
 	}
 
 	printf("i_ext: fofs:%x blkaddr:%x len:%x\n",
-			inode->i_ext.fofs,
-			inode->i_ext.blk_addr,
-			inode->i_ext.len);
+			le32_to_cpu(inode->i_ext.fofs),
+			le32_to_cpu(inode->i_ext.blk_addr),
+			le32_to_cpu(inode->i_ext.len));
 
 	DISP_u32(inode, i_addr[0]);	/* Pointers to data blocks */
 	DISP_u32(inode, i_addr[1]);	/* Pointers to data blocks */
@@ -65,7 +95,7 @@
 	for (i = 4; i < ADDRS_PER_INODE(inode); i++) {
 		if (inode->i_addr[i] != 0x0) {
 			printf("i_addr[0x%x] points data block\r\t\t[0x%4x]\n",
-					i, inode->i_addr[i]);
+					i, le32_to_cpu(inode->i_addr[i]));
 			break;
 		}
 	}
@@ -79,30 +109,37 @@
 	printf("\n");
 }
 
-void print_node_info(struct f2fs_node *node_block)
+void print_node_info(struct f2fs_node *node_block, int verbose)
 {
 	nid_t ino = le32_to_cpu(node_block->footer.ino);
 	nid_t nid = le32_to_cpu(node_block->footer.nid);
 	/* Is this inode? */
 	if (ino == nid) {
-		DBG(0, "Node ID [0x%x:%u] is inode\n", nid, nid);
-		print_inode_info(&node_block->i, 0);
+		DBG(verbose, "Node ID [0x%x:%u] is inode\n", nid, nid);
+		print_inode_info(&node_block->i, verbose);
 	} else {
 		int i;
 		u32 *dump_blk = (u32 *)node_block;
-		DBG(0, "Node ID [0x%x:%u] is direct node or indirect node.\n",
+		DBG(verbose,
+			"Node ID [0x%x:%u] is direct node or indirect node.\n",
 								nid, nid);
 		for (i = 0; i <= 10; i++)
-			MSG(0, "[%d]\t\t\t[0x%8x : %d]\n",
+			MSG(verbose, "[%d]\t\t\t[0x%8x : %d]\n",
 						i, dump_blk[i], dump_blk[i]);
 	}
 }
 
-void print_raw_sb_info(struct f2fs_sb_info *sbi)
+static void DISP_label(u_int16_t *name)
 {
-	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	char buffer[MAX_VOLUME_NAME];
 
-	if (!config.dbg_lv)
+	utf16_to_utf8(buffer, name, MAX_VOLUME_NAME, MAX_VOLUME_NAME);
+	printf("%-30s" "\t\t[%s]\n", "volum_name", buffer);
+}
+
+void print_raw_sb_info(struct f2fs_super_block *sb)
+{
+	if (!c.dbg_lv)
 		return;
 
 	printf("\n");
@@ -112,6 +149,9 @@
 
 	DISP_u32(sb, magic);
 	DISP_u32(sb, major_ver);
+
+	DISP_label(sb->volume_name);
+
 	DISP_u32(sb, minor_ver);
 	DISP_u32(sb, log_sectorsize);
 	DISP_u32(sb, log_sectors_per_block);
@@ -151,7 +191,7 @@
 {
 	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 
-	if (!config.dbg_lv)
+	if (!c.dbg_lv)
 		return;
 
 	printf("\n");
@@ -204,35 +244,173 @@
 	printf("\n\n");
 }
 
-int sanity_check_raw_super(struct f2fs_super_block *raw_super)
+void print_cp_state(u32 flag)
+{
+	MSG(0, "Info: checkpoint state = %x : ", flag);
+	if (flag & CP_FSCK_FLAG)
+		MSG(0, "%s", " fsck");
+	if (flag & CP_ERROR_FLAG)
+		MSG(0, "%s", " error");
+	if (flag & CP_COMPACT_SUM_FLAG)
+		MSG(0, "%s", " compacted_summary");
+	if (flag & CP_ORPHAN_PRESENT_FLAG)
+		MSG(0, "%s", " orphan_inodes");
+	if (flag & CP_FASTBOOT_FLAG)
+		MSG(0, "%s", " fastboot");
+	if (flag & CP_UMOUNT_FLAG)
+		MSG(0, "%s", " unmount");
+	else
+		MSG(0, "%s", " sudden-power-off");
+	MSG(0, "\n");
+}
+
+void print_sb_state(struct f2fs_super_block *sb)
+{
+	__le32 f = sb->feature;
+	int i;
+
+	MSG(0, "Info: superblock features = %x : ", f);
+	if (f & cpu_to_le32(F2FS_FEATURE_ENCRYPT)) {
+		MSG(0, "%s", " encrypt");
+	}
+	if (f & cpu_to_le32(F2FS_FEATURE_BLKZONED)) {
+		MSG(0, "%s", " zoned block device");
+	}
+	MSG(0, "\n");
+	MSG(0, "Info: superblock encrypt level = %d, salt = ",
+					sb->encryption_level);
+	for (i = 0; i < 16; i++)
+		MSG(0, "%02x", sb->encrypt_pw_salt[i]);
+	MSG(0, "\n");
+}
+
+static inline int sanity_check_area_boundary(struct f2fs_super_block *sb,
+							u64 offset)
+{
+	u32 segment0_blkaddr = get_sb(segment0_blkaddr);
+	u32 cp_blkaddr = get_sb(cp_blkaddr);
+	u32 sit_blkaddr = get_sb(sit_blkaddr);
+	u32 nat_blkaddr = get_sb(nat_blkaddr);
+	u32 ssa_blkaddr = get_sb(ssa_blkaddr);
+	u32 main_blkaddr = get_sb(main_blkaddr);
+	u32 segment_count_ckpt = get_sb(segment_count_ckpt);
+	u32 segment_count_sit = get_sb(segment_count_sit);
+	u32 segment_count_nat = get_sb(segment_count_nat);
+	u32 segment_count_ssa = get_sb(segment_count_ssa);
+	u32 segment_count_main = get_sb(segment_count_main);
+	u32 segment_count = get_sb(segment_count);
+	u32 log_blocks_per_seg = get_sb(log_blocks_per_seg);
+	u64 main_end_blkaddr = main_blkaddr +
+				(segment_count_main << log_blocks_per_seg);
+	u64 seg_end_blkaddr = segment0_blkaddr +
+				(segment_count << log_blocks_per_seg);
+
+	if (segment0_blkaddr != cp_blkaddr) {
+		MSG(0, "\tMismatch segment0(%u) cp_blkaddr(%u)\n",
+				segment0_blkaddr, cp_blkaddr);
+		return -1;
+	}
+
+	if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) !=
+							sit_blkaddr) {
+		MSG(0, "\tWrong CP boundary, start(%u) end(%u) blocks(%u)\n",
+			cp_blkaddr, sit_blkaddr,
+			segment_count_ckpt << log_blocks_per_seg);
+		return -1;
+	}
+
+	if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) !=
+							nat_blkaddr) {
+		MSG(0, "\tWrong SIT boundary, start(%u) end(%u) blocks(%u)\n",
+			sit_blkaddr, nat_blkaddr,
+			segment_count_sit << log_blocks_per_seg);
+		return -1;
+	}
+
+	if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) !=
+							ssa_blkaddr) {
+		MSG(0, "\tWrong NAT boundary, start(%u) end(%u) blocks(%u)\n",
+			nat_blkaddr, ssa_blkaddr,
+			segment_count_nat << log_blocks_per_seg);
+		return -1;
+	}
+
+	if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) !=
+							main_blkaddr) {
+		MSG(0, "\tWrong SSA boundary, start(%u) end(%u) blocks(%u)\n",
+			ssa_blkaddr, main_blkaddr,
+			segment_count_ssa << log_blocks_per_seg);
+		return -1;
+	}
+
+	if (main_end_blkaddr > seg_end_blkaddr) {
+		MSG(0, "\tWrong MAIN_AREA, start(%u) end(%u) block(%u)\n",
+			main_blkaddr,
+			segment0_blkaddr +
+				(segment_count << log_blocks_per_seg),
+			segment_count_main << log_blocks_per_seg);
+		return -1;
+	} else if (main_end_blkaddr < seg_end_blkaddr) {
+		int err;
+
+		set_sb(segment_count, (main_end_blkaddr -
+				segment0_blkaddr) >> log_blocks_per_seg);
+
+		err = dev_write(sb, offset, sizeof(struct f2fs_super_block));
+		MSG(0, "Info: Fix alignment: %s, start(%u) end(%u) block(%u)\n",
+			err ? "failed": "done",
+			main_blkaddr,
+			segment0_blkaddr +
+				(segment_count << log_blocks_per_seg),
+			segment_count_main << log_blocks_per_seg);
+	}
+	return 0;
+}
+
+int sanity_check_raw_super(struct f2fs_super_block *sb, u64 offset)
 {
 	unsigned int blocksize;
 
-	if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) {
+	if (F2FS_SUPER_MAGIC != get_sb(magic))
+		return -1;
+
+	if (F2FS_BLKSIZE != PAGE_CACHE_SIZE)
+		return -1;
+
+	blocksize = 1 << get_sb(log_blocksize);
+	if (F2FS_BLKSIZE != blocksize)
+		return -1;
+
+	/* check log blocks per segment */
+	if (get_sb(log_blocks_per_seg) != 9)
+		return -1;
+
+	/* Currently, support 512/1024/2048/4096 bytes sector size */
+	if (get_sb(log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE ||
+			get_sb(log_sectorsize) < F2FS_MIN_LOG_SECTOR_SIZE)
+		return -1;
+
+	if (get_sb(log_sectors_per_block) + get_sb(log_sectorsize) !=
+						F2FS_MAX_LOG_SECTOR_SIZE)
+		return -1;
+
+	/* check reserved ino info */
+	if (get_sb(node_ino) != 1 || get_sb(meta_ino) != 2 ||
+					get_sb(root_ino) != 3)
+		return -1;
+
+	/* Check zoned block device feature */
+	if (c.devices[0].zoned_model == F2FS_ZONED_HM &&
+			!(sb->feature & cpu_to_le32(F2FS_FEATURE_BLKZONED))) {
+		MSG(0, "\tMissing zoned block device feature\n");
 		return -1;
 	}
 
-	if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) {
+	if (get_sb(segment_count) > F2FS_MAX_SEGMENT)
 		return -1;
-	}
 
-	blocksize = 1 << le32_to_cpu(raw_super->log_blocksize);
-	if (F2FS_BLKSIZE != blocksize) {
+	if (sanity_check_area_boundary(sb, offset))
 		return -1;
-	}
-
-	if (le32_to_cpu(raw_super->log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE ||
-		le32_to_cpu(raw_super->log_sectorsize) <
-						F2FS_MIN_LOG_SECTOR_SIZE) {
-		return -1;
-	}
-
-	if (le32_to_cpu(raw_super->log_sectors_per_block) +
-				le32_to_cpu(raw_super->log_sectorsize) !=
-						F2FS_MAX_LOG_SECTOR_SIZE) {
-		return -1;
-	}
-
 	return 0;
 }
 
@@ -250,40 +428,42 @@
 	if (dev_read(sbi->raw_super, offset, sizeof(struct f2fs_super_block)))
 		return -1;
 
-	if (!sanity_check_raw_super(sbi->raw_super)) {
+	if (!sanity_check_raw_super(sbi->raw_super, offset)) {
 		/* get kernel version */
-		if (config.kd >= 0) {
-			dev_read_version(config.version, 0, VERSION_LEN);
-			get_kernel_version(config.version);
+		if (c.kd >= 0) {
+			dev_read_version(c.version, 0, VERSION_LEN);
+			get_kernel_version(c.version);
 		} else {
-			memset(config.version, 0, VERSION_LEN);
+			memset(c.version, 0, VERSION_LEN);
 		}
 
 		/* build sb version */
-		memcpy(config.sb_version, sbi->raw_super->version, VERSION_LEN);
-		get_kernel_version(config.sb_version);
-		memcpy(config.init_version, sbi->raw_super->init_version, VERSION_LEN);
-		get_kernel_version(config.init_version);
+		memcpy(c.sb_version, sbi->raw_super->version, VERSION_LEN);
+		get_kernel_version(c.sb_version);
+		memcpy(c.init_version, sbi->raw_super->init_version, VERSION_LEN);
+		get_kernel_version(c.init_version);
 
-		MSG(0, "Info: MKFS version\n  \"%s\"\n", config.init_version);
+		MSG(0, "Info: MKFS version\n  \"%s\"\n", c.init_version);
 		MSG(0, "Info: FSCK version\n  from \"%s\"\n    to \"%s\"\n",
-					config.sb_version, config.version);
-		if (memcmp(config.sb_version, config.version, VERSION_LEN)) {
+					c.sb_version, c.version);
+		if (memcmp(c.sb_version, c.version, VERSION_LEN)) {
 			int ret;
 
 			memcpy(sbi->raw_super->version,
-						config.version, VERSION_LEN);
+						c.version, VERSION_LEN);
 			ret = dev_write(sbi->raw_super, offset,
 					sizeof(struct f2fs_super_block));
 			ASSERT(ret >= 0);
 
-			config.auto_fix = 0;
-			config.fix_on = 1;
+			c.auto_fix = 0;
+			c.fix_on = 1;
 		}
+		print_sb_state(sbi->raw_super);
 		return 0;
 	}
 
 	free(sbi->raw_super);
+	sbi->raw_super = NULL;
 	MSG(0, "\tCan't find a valid F2FS superblock at 0x%x\n", block);
 
 	return -EINVAL;
@@ -291,24 +471,60 @@
 
 int init_sb_info(struct f2fs_sb_info *sbi)
 {
-	struct f2fs_super_block *raw_super = sbi->raw_super;
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	u64 total_sectors;
+	int i;
 
-	sbi->log_sectors_per_block =
-		le32_to_cpu(raw_super->log_sectors_per_block);
-	sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize);
+	sbi->log_sectors_per_block = get_sb(log_sectors_per_block);
+	sbi->log_blocksize = get_sb(log_blocksize);
 	sbi->blocksize = 1 << sbi->log_blocksize;
-	sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
+	sbi->log_blocks_per_seg = get_sb(log_blocks_per_seg);
 	sbi->blocks_per_seg = 1 << sbi->log_blocks_per_seg;
-	sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec);
-	sbi->secs_per_zone = le32_to_cpu(raw_super->secs_per_zone);
-	sbi->total_sections = le32_to_cpu(raw_super->section_count);
-	sbi->total_node_count =
-		(le32_to_cpu(raw_super->segment_count_nat) / 2)
-		* sbi->blocks_per_seg * NAT_ENTRY_PER_BLOCK;
-	sbi->root_ino_num = le32_to_cpu(raw_super->root_ino);
-	sbi->node_ino_num = le32_to_cpu(raw_super->node_ino);
-	sbi->meta_ino_num = le32_to_cpu(raw_super->meta_ino);
+	sbi->segs_per_sec = get_sb(segs_per_sec);
+	sbi->secs_per_zone = get_sb(secs_per_zone);
+	sbi->total_sections = get_sb(section_count);
+	sbi->total_node_count = (get_sb(segment_count_nat) / 2) *
+				sbi->blocks_per_seg * NAT_ENTRY_PER_BLOCK;
+	sbi->root_ino_num = get_sb(root_ino);
+	sbi->node_ino_num = get_sb(node_ino);
+	sbi->meta_ino_num = get_sb(meta_ino);
 	sbi->cur_victim_sec = NULL_SEGNO;
+
+	for (i = 0; i < MAX_DEVICES; i++) {
+		if (!sb->devs[i].path[0])
+			break;
+
+		if (i) {
+			c.devices[i].path = strdup((char *)sb->devs[i].path);
+			if (get_device_info(i))
+				ASSERT(0);
+		} else {
+			ASSERT(!strcmp((char *)sb->devs[i].path,
+						(char *)c.devices[i].path));
+		}
+
+		c.devices[i].total_segments =
+			le32_to_cpu(sb->devs[i].total_segments);
+		if (i)
+			c.devices[i].start_blkaddr =
+				c.devices[i - 1].end_blkaddr + 1;
+		c.devices[i].end_blkaddr = c.devices[i].start_blkaddr +
+			c.devices[i].total_segments *
+			c.blks_per_seg - 1;
+		if (i == 0)
+			c.devices[i].end_blkaddr += get_sb(segment0_blkaddr);
+
+		c.ndevs = i + 1;
+		MSG(0, "Info: Device[%d] : %s blkaddr = %"PRIx64"--%"PRIx64"\n",
+				i, c.devices[i].path,
+				c.devices[i].start_blkaddr,
+				c.devices[i].end_blkaddr);
+	}
+
+	total_sectors = get_sb(block_count) << sbi->log_sectors_per_block;
+	MSG(0, "Info: total FS sectors = %"PRIu64" (%"PRIu64" MB)\n",
+				total_sectors, total_sectors >>
+						(20 - get_sb(log_sectorsize)));
 	return 0;
 }
 
@@ -316,7 +532,7 @@
 				unsigned long long *version)
 {
 	void *cp_page_1, *cp_page_2;
-	struct f2fs_checkpoint *cp_block;
+	struct f2fs_checkpoint *cp;
 	unsigned long blk_size = sbi->blocksize;
 	unsigned long long cur_version = 0, pre_version = 0;
 	unsigned int crc = 0;
@@ -325,36 +541,36 @@
 	/* Read the 1st cp block in this CP pack */
 	cp_page_1 = malloc(PAGE_SIZE);
 	if (dev_read_block(cp_page_1, cp_addr) < 0)
-		return NULL;
+		goto invalid_cp1;
 
-	cp_block = (struct f2fs_checkpoint *)cp_page_1;
-	crc_offset = le32_to_cpu(cp_block->checksum_offset);
+	cp = (struct f2fs_checkpoint *)cp_page_1;
+	crc_offset = get_cp(checksum_offset);
 	if (crc_offset >= blk_size)
 		goto invalid_cp1;
 
-	crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset);
-	if (f2fs_crc_valid(crc, cp_block, crc_offset))
+	crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
+	if (f2fs_crc_valid(crc, cp, crc_offset))
 		goto invalid_cp1;
 
-	pre_version = le64_to_cpu(cp_block->checkpoint_ver);
+	pre_version = get_cp(checkpoint_ver);
 
 	/* Read the 2nd cp block in this CP pack */
 	cp_page_2 = malloc(PAGE_SIZE);
-	cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1;
+	cp_addr += get_cp(cp_pack_total_block_count) - 1;
 
 	if (dev_read_block(cp_page_2, cp_addr) < 0)
 		goto invalid_cp2;
 
-	cp_block = (struct f2fs_checkpoint *)cp_page_2;
-	crc_offset = le32_to_cpu(cp_block->checksum_offset);
+	cp = (struct f2fs_checkpoint *)cp_page_2;
+	crc_offset = get_cp(checksum_offset);
 	if (crc_offset >= blk_size)
 		goto invalid_cp2;
 
-	crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset);
-	if (f2fs_crc_valid(crc, cp_block, crc_offset))
+	crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
+	if (f2fs_crc_valid(crc, cp, crc_offset))
 		goto invalid_cp2;
 
-	cur_version = le64_to_cpu(cp_block->checkpoint_ver);
+	cur_version = get_cp(checkpoint_ver);
 
 	if (cur_version == pre_version) {
 		*version = cur_version;
@@ -371,14 +587,19 @@
 
 int get_valid_checkpoint(struct f2fs_sb_info *sbi)
 {
-	struct f2fs_super_block *raw_sb = sbi->raw_super;
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	void *cp1, *cp2, *cur_page;
 	unsigned long blk_size = sbi->blocksize;
-	unsigned long long cp1_version = 0, cp2_version = 0;
+	unsigned long long cp1_version = 0, cp2_version = 0, version;
 	unsigned long long cp_start_blk_no;
-	unsigned int cp_blks = 1 + le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
+	unsigned int cp_payload, cp_blks;
 	int ret;
 
+	cp_payload = get_sb(cp_payload);
+	if (cp_payload > F2FS_BLK_ALIGN(MAX_SIT_BITMAP_SIZE))
+		return -EINVAL;
+
+	cp_blks = 1 + cp_payload;
 	sbi->ckpt = malloc(cp_blks * blk_size);
 	if (!sbi->ckpt)
 		return -ENOMEM;
@@ -386,32 +607,35 @@
 	 * Finding out valid cp block involves read both
 	 * sets( cp pack1 and cp pack 2)
 	 */
-	cp_start_blk_no = le32_to_cpu(raw_sb->cp_blkaddr);
+	cp_start_blk_no = get_sb(cp_blkaddr);
 	cp1 = validate_checkpoint(sbi, cp_start_blk_no, &cp1_version);
 
 	/* The second checkpoint pack should start at the next segment */
-	cp_start_blk_no += 1 << le32_to_cpu(raw_sb->log_blocks_per_seg);
+	cp_start_blk_no += 1 << get_sb(log_blocks_per_seg);
 	cp2 = validate_checkpoint(sbi, cp_start_blk_no, &cp2_version);
 
 	if (cp1 && cp2) {
 		if (ver_after(cp2_version, cp1_version)) {
 			cur_page = cp2;
 			sbi->cur_cp = 2;
+			version = cp2_version;
 		} else {
 			cur_page = cp1;
 			sbi->cur_cp = 1;
+			version = cp1_version;
 		}
 	} else if (cp1) {
 		cur_page = cp1;
 		sbi->cur_cp = 1;
+		version = cp1_version;
 	} else if (cp2) {
 		cur_page = cp2;
 		sbi->cur_cp = 2;
-	} else {
-		free(cp1);
-		free(cp2);
+		version = cp2_version;
+	} else
 		goto fail_no_cp;
-	}
+
+	MSG(0, "Info: CKPT version = %llx\n", version);
 
 	memcpy(sbi->ckpt, cur_page, blk_size);
 
@@ -419,10 +643,10 @@
 		unsigned int i;
 		unsigned long long cp_blk_no;
 
-		cp_blk_no = le32_to_cpu(raw_sb->cp_blkaddr);
+		cp_blk_no = get_sb(cp_blkaddr);
 		if (cur_page == cp2)
-			cp_blk_no += 1 <<
-				le32_to_cpu(raw_sb->log_blocks_per_seg);
+			cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+
 		/* copy sit bitmap */
 		for (i = 1; i < cp_blks; i++) {
 			unsigned char *ckpt = (unsigned char *)sbi->ckpt;
@@ -431,27 +655,30 @@
 			memcpy(ckpt + i * blk_size, cur_page, blk_size);
 		}
 	}
-	free(cp1);
-	free(cp2);
+	if (cp1)
+		free(cp1);
+	if (cp2)
+		free(cp2);
 	return 0;
 
 fail_no_cp:
 	free(sbi->ckpt);
+	sbi->ckpt = NULL;
 	return -EINVAL;
 }
 
 int sanity_check_ckpt(struct f2fs_sb_info *sbi)
 {
 	unsigned int total, fsmeta;
-	struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 
-	total = le32_to_cpu(raw_super->segment_count);
-	fsmeta = le32_to_cpu(raw_super->segment_count_ckpt);
-	fsmeta += le32_to_cpu(raw_super->segment_count_sit);
-	fsmeta += le32_to_cpu(raw_super->segment_count_nat);
-	fsmeta += le32_to_cpu(ckpt->rsvd_segment_count);
-	fsmeta += le32_to_cpu(raw_super->segment_count_ssa);
+	total = get_sb(segment_count);
+	fsmeta = get_sb(segment_count_ckpt);
+	fsmeta += get_sb(segment_count_sit);
+	fsmeta += get_sb(segment_count_nat);
+	fsmeta += get_cp(rsvd_segment_count);
+	fsmeta += get_sb(segment_count_ssa);
 
 	if (fsmeta >= total)
 		return 1;
@@ -459,23 +686,92 @@
 	return 0;
 }
 
+static pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
+{
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	pgoff_t block_off;
+	pgoff_t block_addr;
+	int seg_off;
+
+	block_off = NAT_BLOCK_OFFSET(start);
+	seg_off = block_off >> sbi->log_blocks_per_seg;
+
+	block_addr = (pgoff_t)(nm_i->nat_blkaddr +
+			(seg_off << sbi->log_blocks_per_seg << 1) +
+			(block_off & ((1 << sbi->log_blocks_per_seg) -1)));
+
+	if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
+		block_addr += sbi->blocks_per_seg;
+
+	return block_addr;
+}
+
+static int f2fs_init_nid_bitmap(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	int nid_bitmap_size = (nm_i->max_nid + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
+	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
+	struct f2fs_summary_block *sum = curseg->sum_blk;
+	struct f2fs_journal *journal = &sum->journal;
+	struct f2fs_nat_block nat_block;
+	block_t start_blk;
+	nid_t nid;
+	int i;
+
+	if (!(c.func == SLOAD))
+		return 0;
+
+	nm_i->nid_bitmap = (char *)calloc(nid_bitmap_size, 1);
+	if (!nm_i->nid_bitmap)
+		return -ENOMEM;
+
+	/* arbitrarily set 0 bit */
+	f2fs_set_bit(0, nm_i->nid_bitmap);
+
+	memset((void *)&nat_block, 0, sizeof(struct f2fs_nat_block));
+
+	for (nid = 0; nid < nm_i->max_nid; nid++) {
+		if (!(nid % NAT_ENTRY_PER_BLOCK)) {
+			int ret;
+
+			start_blk = current_nat_addr(sbi, nid);
+			ret = dev_read_block((void *)&nat_block, start_blk);
+			ASSERT(ret >= 0);
+		}
+
+		if (nat_block.entries[nid % NAT_ENTRY_PER_BLOCK].block_addr)
+			f2fs_set_bit(nid, nm_i->nid_bitmap);
+	}
+
+	for (i = 0; i < nats_in_cursum(journal); i++) {
+		block_t addr;
+
+		addr = le32_to_cpu(nat_in_journal(journal, i).block_addr);
+		nid = le32_to_cpu(nid_in_journal(journal, i));
+		if (addr != NULL_ADDR)
+			f2fs_set_bit(nid, nm_i->nid_bitmap);
+	}
+	return 0;
+}
+
 int init_node_manager(struct f2fs_sb_info *sbi)
 {
-	struct f2fs_super_block *sb_raw = F2FS_RAW_SUPER(sbi);
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 	struct f2fs_nm_info *nm_i = NM_I(sbi);
 	unsigned char *version_bitmap;
 	unsigned int nat_segs, nat_blocks;
 
-	nm_i->nat_blkaddr = le32_to_cpu(sb_raw->nat_blkaddr);
+	nm_i->nat_blkaddr = get_sb(nat_blkaddr);
 
 	/* segment_count_nat includes pair segment so divide to 2. */
-	nat_segs = le32_to_cpu(sb_raw->segment_count_nat) >> 1;
-	nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg);
+	nat_segs = get_sb(segment_count_nat) >> 1;
+	nat_blocks = nat_segs << get_sb(log_blocks_per_seg);
 	nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
 	nm_i->fcnt = 0;
 	nm_i->nat_cnt = 0;
-	nm_i->init_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
-	nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
+	nm_i->init_scan_nid = get_cp(next_free_nid);
+	nm_i->next_scan_nid = get_cp(next_free_nid);
 
 	nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
 
@@ -488,7 +784,7 @@
 
 	/* copy version bitmap */
 	memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size);
-	return 0;
+	return f2fs_init_nid_bitmap(sbi);
 }
 
 int build_node_manager(struct f2fs_sb_info *sbi)
@@ -507,8 +803,8 @@
 
 int build_sit_info(struct f2fs_sb_info *sbi)
 {
-	struct f2fs_super_block *raw_sb = F2FS_RAW_SUPER(sbi);
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 	struct sit_info *sit_i;
 	unsigned int sit_segs, start;
 	char *src_bitmap, *dst_bitmap;
@@ -521,6 +817,8 @@
 	SM_I(sbi)->sit_info = sit_i;
 
 	sit_i->sentries = calloc(TOTAL_SEGS(sbi) * sizeof(struct seg_entry), 1);
+	if (!sit_i->sentries)
+		return -ENOMEM;
 
 	for (start = 0; start < TOTAL_SEGS(sbi); start++) {
 		sit_i->sentries[start].cur_valid_map
@@ -532,21 +830,21 @@
 			return -ENOMEM;
 	}
 
-	sit_segs = le32_to_cpu(raw_sb->segment_count_sit) >> 1;
+	sit_segs = get_sb(segment_count_sit) >> 1;
 	bitmap_size = __bitmap_size(sbi, SIT_BITMAP);
 	src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP);
 
 	dst_bitmap = malloc(bitmap_size);
 	memcpy(dst_bitmap, src_bitmap, bitmap_size);
 
-	sit_i->sit_base_addr = le32_to_cpu(raw_sb->sit_blkaddr);
+	sit_i->sit_base_addr = get_sb(sit_blkaddr);
 	sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg;
-	sit_i->written_valid_blocks = le64_to_cpu(ckpt->valid_block_count);
+	sit_i->written_valid_blocks = get_cp(valid_block_count);
 	sit_i->sit_bitmap = dst_bitmap;
 	sit_i->bitmap_size = bitmap_size;
 	sit_i->dirty_sentries = 0;
 	sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK;
-	sit_i->elapsed_time = le64_to_cpu(ckpt->elapsed_time);
+	sit_i->elapsed_time = get_cp(elapsed_time);
 	return 0;
 }
 
@@ -581,10 +879,10 @@
 	ASSERT(ret >= 0);
 
 	curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
-	memcpy(&curseg->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE);
+	memcpy(&curseg->sum_blk->journal.n_nats, kaddr, SUM_JOURNAL_SIZE);
 
 	curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
-	memcpy(&curseg->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE,
+	memcpy(&curseg->sum_blk->journal.n_sits, kaddr + SUM_JOURNAL_SIZE,
 						SUM_JOURNAL_SIZE);
 
 	offset = 2 * SUM_JOURNAL_SIZE;
@@ -599,6 +897,8 @@
 		else
 			blk_off = curseg->next_blkoff;
 
+		ASSERT(blk_off <= ENTRIES_IN_SUM);
+
 		for (j = 0; j < blk_off; j++) {
 			struct f2fs_summary *s;
 			s = (struct f2fs_summary *)(kaddr + offset);
@@ -643,7 +943,7 @@
 
 static void read_normal_summaries(struct f2fs_sb_info *sbi, int type)
 {
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 	struct f2fs_summary_block *sum_blk;
 	struct curseg_info *curseg;
 	unsigned int segno = 0;
@@ -651,15 +951,14 @@
 	int ret;
 
 	if (IS_DATASEG(type)) {
-		segno = le32_to_cpu(ckpt->cur_data_segno[type]);
-		if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG))
+		segno = get_cp(cur_data_segno[type]);
+		if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG))
 			blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type);
 		else
 			blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type);
 	} else {
-		segno = le32_to_cpu(ckpt->cur_node_segno[type -
-							CURSEG_HOT_NODE]);
-		if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG))
+		segno = get_cp(cur_node_segno[type - CURSEG_HOT_NODE]);
+		if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG))
 			blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE,
 							type - CURSEG_HOT_NODE);
 		else
@@ -670,7 +969,7 @@
 	ret = dev_read_block(sum_blk, blk_addr);
 	ASSERT(ret >= 0);
 
-	if (IS_NODESEG(type) && !is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG))
+	if (IS_NODESEG(type) && !is_set_ckpt_flags(cp, CP_UMOUNT_FLAG))
 		restore_node_summary(sbi, segno, sum_blk);
 
 	curseg = CURSEG_I(sbi, type);
@@ -679,6 +978,36 @@
 	free(sum_blk);
 }
 
+void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr,
+					struct f2fs_summary *sum)
+{
+	struct f2fs_summary_block *sum_blk;
+	u32 segno, offset;
+	int type, ret;
+	struct seg_entry *se;
+
+	segno = GET_SEGNO(sbi, blk_addr);
+	offset = OFFSET_IN_SEG(sbi, blk_addr);
+
+	se = get_seg_entry(sbi, segno);
+
+	sum_blk = get_sum_block(sbi, segno, &type);
+	memcpy(&sum_blk->entries[offset], sum, sizeof(*sum));
+	sum_blk->footer.entry_type = IS_NODESEG(se->type) ? SUM_TYPE_NODE :
+							SUM_TYPE_DATA;
+
+	/* write SSA all the time */
+	if (type < SEG_TYPE_MAX) {
+		u64 ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+		ret = dev_write_block(sum_blk, ssa_blk);
+		ASSERT(ret >= 0);
+	}
+
+	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+					type == SEG_TYPE_MAX)
+		free(sum_blk);
+}
+
 static void restore_curseg_summaries(struct f2fs_sb_info *sbi)
 {
 	int type = CURSEG_HOT_DATA;
@@ -694,7 +1023,7 @@
 
 static void build_curseg(struct f2fs_sb_info *sbi)
 {
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 	struct curseg_info *array;
 	unsigned short blk_off;
 	unsigned int segno;
@@ -709,39 +1038,42 @@
 		array[i].sum_blk = malloc(PAGE_CACHE_SIZE);
 		ASSERT(array[i].sum_blk);
 		if (i <= CURSEG_COLD_DATA) {
-			blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]);
-			segno = le32_to_cpu(ckpt->cur_data_segno[i]);
+			blk_off = get_cp(cur_data_blkoff[i]);
+			segno = get_cp(cur_data_segno[i]);
 		}
 		if (i > CURSEG_COLD_DATA) {
-			blk_off = le16_to_cpu(ckpt->cur_node_blkoff[i -
-							CURSEG_HOT_NODE]);
-			segno = le32_to_cpu(ckpt->cur_node_segno[i -
-							CURSEG_HOT_NODE]);
+			blk_off = get_cp(cur_node_blkoff[i - CURSEG_HOT_NODE]);
+			segno = get_cp(cur_node_segno[i - CURSEG_HOT_NODE]);
 		}
+		ASSERT(segno < TOTAL_SEGS(sbi));
+		ASSERT(blk_off < DEFAULT_BLOCKS_PER_SEGMENT);
+
 		array[i].segno = segno;
 		array[i].zone = GET_ZONENO_FROM_SEGNO(sbi, segno);
 		array[i].next_segno = NULL_SEGNO;
 		array[i].next_blkoff = blk_off;
-		array[i].alloc_type = ckpt->alloc_type[i];
+		array[i].alloc_type = cp->alloc_type[i];
 	}
 	restore_curseg_summaries(sbi);
 }
 
-inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
+static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
 {
 	unsigned int end_segno = SM_I(sbi)->segment_count - 1;
 	ASSERT(segno <= end_segno);
 }
 
-static struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi,
+struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi,
 						unsigned int segno)
 {
 	struct sit_info *sit_i = SIT_I(sbi);
 	unsigned int offset = SIT_BLOCK_OFFSET(sit_i, segno);
 	block_t blk_addr = sit_i->sit_base_addr + offset;
-	struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1);
+	struct f2fs_sit_block *sit_blk;
 	int ret;
 
+	sit_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(sit_blk);
 	check_seg_range(sbi, segno);
 
 	/* calculate sit block address */
@@ -819,53 +1151,60 @@
 	return &sit_i->sentries[segno];
 }
 
-int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno,
-				struct f2fs_summary_block *sum_blk)
+struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *sbi,
+				unsigned int segno, int *ret_type)
 {
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	struct f2fs_summary_block *sum_blk;
 	struct curseg_info *curseg;
 	int type, ret;
 	u64 ssa_blk;
 
+	*ret_type= SEG_TYPE_MAX;
+
 	ssa_blk = GET_SUM_BLKADDR(sbi, segno);
 	for (type = 0; type < NR_CURSEG_NODE_TYPE; type++) {
-		if (segno == ckpt->cur_node_segno[type]) {
+		if (segno == get_cp(cur_node_segno[type])) {
 			curseg = CURSEG_I(sbi, CURSEG_HOT_NODE + type);
 			if (!IS_SUM_NODE_SEG(curseg->sum_blk->footer)) {
 				ASSERT_MSG("segno [0x%x] indicates a data "
 						"segment, but should be node",
 						segno);
-				return -EINVAL;
+				*ret_type = -SEG_TYPE_CUR_NODE;
+			} else {
+				*ret_type = SEG_TYPE_CUR_NODE;
 			}
-			memcpy(sum_blk, curseg->sum_blk, BLOCK_SZ);
-			return SEG_TYPE_CUR_NODE;
+			return curseg->sum_blk;
 		}
 	}
 
 	for (type = 0; type < NR_CURSEG_DATA_TYPE; type++) {
-		if (segno == ckpt->cur_data_segno[type]) {
+		if (segno == get_cp(cur_data_segno[type])) {
 			curseg = CURSEG_I(sbi, type);
 			if (IS_SUM_NODE_SEG(curseg->sum_blk->footer)) {
 				ASSERT_MSG("segno [0x%x] indicates a node "
 						"segment, but should be data",
 						segno);
-				return -EINVAL;
+				*ret_type = -SEG_TYPE_CUR_DATA;
+			} else {
+				*ret_type = SEG_TYPE_CUR_DATA;
 			}
-			DBG(2, "segno [0x%x] is current data seg[0x%x]\n",
-								segno, type);
-			memcpy(sum_blk, curseg->sum_blk, BLOCK_SZ);
-			return SEG_TYPE_CUR_DATA;
+			return curseg->sum_blk;
 		}
 	}
 
+	sum_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(sum_blk);
+
 	ret = dev_read_block(sum_blk, ssa_blk);
 	ASSERT(ret >= 0);
 
 	if (IS_SUM_NODE_SEG(sum_blk->footer))
-		return SEG_TYPE_NODE;
-	else
-		return SEG_TYPE_DATA;
+		*ret_type = SEG_TYPE_NODE;
+	else if (IS_SUM_DATA_SEG(sum_blk->footer))
+		*ret_type = SEG_TYPE_DATA;
 
+	return sum_blk;
 }
 
 int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr,
@@ -873,45 +1212,36 @@
 {
 	struct f2fs_summary_block *sum_blk;
 	u32 segno, offset;
-	int ret;
+	int type;
 
 	segno = GET_SEGNO(sbi, blk_addr);
 	offset = OFFSET_IN_SEG(sbi, blk_addr);
 
-	sum_blk = calloc(BLOCK_SZ, 1);
-
-	ret = get_sum_block(sbi, segno, sum_blk);
+	sum_blk = get_sum_block(sbi, segno, &type);
 	memcpy(sum_entry, &(sum_blk->entries[offset]),
 				sizeof(struct f2fs_summary));
-	free(sum_blk);
-	return ret;
+	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+					type == SEG_TYPE_MAX)
+		free(sum_blk);
+	return type;
 }
 
 static void get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
 				struct f2fs_nat_entry *raw_nat)
 {
-	struct f2fs_nm_info *nm_i = NM_I(sbi);
 	struct f2fs_nat_block *nat_block;
-	pgoff_t block_off;
 	pgoff_t block_addr;
-	int seg_off, entry_off;
+	int entry_off;
 	int ret;
 
 	if (lookup_nat_in_journal(sbi, nid, raw_nat) >= 0)
 		return;
 
 	nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+	ASSERT(nat_block);
 
-	block_off = nid / NAT_ENTRY_PER_BLOCK;
 	entry_off = nid % NAT_ENTRY_PER_BLOCK;
-
-	seg_off = block_off >> sbi->log_blocks_per_seg;
-	block_addr = (pgoff_t)(nm_i->nat_blkaddr +
-			(seg_off << sbi->log_blocks_per_seg << 1) +
-			(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
-
-	if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
-		block_addr += sbi->blocks_per_seg;
+	block_addr = current_nat_addr(sbi, nid);
 
 	ret = dev_read_block(nat_block, block_addr);
 	ASSERT(ret >= 0);
@@ -921,6 +1251,82 @@
 	free(nat_block);
 }
 
+void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid,
+				u16 ofs_in_node, block_t newaddr)
+{
+	struct f2fs_node *node_blk = NULL;
+	struct node_info ni;
+	block_t oldaddr, startaddr, endaddr;
+	int ret;
+
+	node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+	ASSERT(node_blk);
+
+	get_node_info(sbi, nid, &ni);
+
+	/* read node_block */
+	ret = dev_read_block(node_blk, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	/* check its block address */
+	if (node_blk->footer.nid == node_blk->footer.ino) {
+		oldaddr = le32_to_cpu(node_blk->i.i_addr[ofs_in_node]);
+		node_blk->i.i_addr[ofs_in_node] = cpu_to_le32(newaddr);
+	} else {
+		oldaddr = le32_to_cpu(node_blk->dn.addr[ofs_in_node]);
+		node_blk->dn.addr[ofs_in_node] = cpu_to_le32(newaddr);
+	}
+
+	ret = dev_write_block(node_blk, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	/* check extent cache entry */
+	if (node_blk->footer.nid != node_blk->footer.ino) {
+		get_node_info(sbi, le32_to_cpu(node_blk->footer.ino), &ni);
+
+		/* read inode block */
+		ret = dev_read_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
+	startaddr = le32_to_cpu(node_blk->i.i_ext.blk_addr);
+	endaddr = startaddr + le32_to_cpu(node_blk->i.i_ext.len);
+	if (oldaddr >= startaddr && oldaddr < endaddr) {
+		node_blk->i.i_ext.len = 0;
+
+		/* update inode block */
+		ret = dev_write_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+	free(node_blk);
+}
+
+void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino,
+					nid_t nid, block_t newaddr)
+{
+	struct f2fs_nat_block *nat_block;
+	pgoff_t block_addr;
+	int entry_off;
+	int ret;
+
+	nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+	ASSERT(nat_block);
+
+	entry_off = nid % NAT_ENTRY_PER_BLOCK;
+	block_addr = current_nat_addr(sbi, nid);
+
+	ret = dev_read_block(nat_block, block_addr);
+	ASSERT(ret >= 0);
+
+	if (ino)
+		nat_block->entries[entry_off].ino = cpu_to_le32(ino);
+	nat_block->entries[entry_off].block_addr = cpu_to_le32(newaddr);
+
+	ret = dev_write_block(nat_block, block_addr);
+	ASSERT(ret >= 0);
+	free(nat_block);
+}
+
 void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
 {
 	struct f2fs_nat_entry raw_nat;
@@ -933,25 +1339,28 @@
 {
 	struct sit_info *sit_i = SIT_I(sbi);
 	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
-	struct f2fs_summary_block *sum = curseg->sum_blk;
-	unsigned int segno;
+	struct f2fs_journal *journal = &curseg->sum_blk->journal;
+	struct seg_entry *se;
+	struct f2fs_sit_entry sit;
+	unsigned int i, segno;
 
 	for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
-		struct seg_entry *se = &sit_i->sentries[segno];
+		se = &sit_i->sentries[segno];
 		struct f2fs_sit_block *sit_blk;
-		struct f2fs_sit_entry sit;
-		int i;
 
-		for (i = 0; i < sits_in_cursum(sum); i++) {
-			if (le32_to_cpu(segno_in_journal(sum, i)) == segno) {
-				sit = sit_in_journal(sum, i);
-				goto got_it;
-			}
-		}
 		sit_blk = get_current_sit_page(sbi, segno);
 		sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)];
 		free(sit_blk);
-got_it:
+
+		check_block_count(sbi, segno, &sit);
+		seg_info_from_raw_sit(se, &sit);
+	}
+
+	for (i = 0; i < sits_in_cursum(journal); i++) {
+		segno = le32_to_cpu(segno_in_journal(journal, i));
+		se = &sit_i->sentries[segno];
+		sit = sit_in_journal(journal, i);
+
 		check_block_count(sbi, segno, &sit);
 		seg_info_from_raw_sit(se, &sit);
 	}
@@ -960,8 +1369,8 @@
 
 int build_segment_manager(struct f2fs_sb_info *sbi)
 {
-	struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
-	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 	struct f2fs_sm_info *sm_info;
 
 	sm_info = malloc(sizeof(struct f2fs_sm_info));
@@ -970,13 +1379,13 @@
 
 	/* init sm info */
 	sbi->sm_info = sm_info;
-	sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr);
-	sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr);
-	sm_info->segment_count = le32_to_cpu(raw_super->segment_count);
-	sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count);
-	sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count);
-	sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main);
-	sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr);
+	sm_info->seg0_blkaddr = get_sb(segment0_blkaddr);
+	sm_info->main_blkaddr = get_sb(main_blkaddr);
+	sm_info->segment_count = get_sb(segment_count);
+	sm_info->reserved_segments = get_cp(rsvd_segment_count);
+	sm_info->ovp_segments = get_cp(overprov_segment_count);
+	sm_info->main_segments = get_sb(segment_count_main);
+	sm_info->ssa_blkaddr = get_sb(ssa_blkaddr);
 
 	build_sit_info(sbi);
 
@@ -999,6 +1408,7 @@
 
 	fsck->sit_area_bitmap_sz = sm_i->main_segments * SIT_VBLOCK_MAP_SIZE;
 	fsck->sit_area_bitmap = calloc(1, fsck->sit_area_bitmap_sz);
+	ASSERT(fsck->sit_area_bitmap);
 	ptr = fsck->sit_area_bitmap;
 
 	ASSERT(fsck->sit_area_bitmap_sz == fsck->main_area_bitmap_sz);
@@ -1010,12 +1420,12 @@
 		ptr += SIT_VBLOCK_MAP_SIZE;
 
 		if (se->valid_blocks == 0x0) {
-			if (sbi->ckpt->cur_node_segno[0] == segno ||
-					sbi->ckpt->cur_data_segno[0] == segno ||
-					sbi->ckpt->cur_node_segno[1] == segno ||
-					sbi->ckpt->cur_data_segno[1] == segno ||
-					sbi->ckpt->cur_node_segno[2] == segno ||
-					sbi->ckpt->cur_data_segno[2] == segno) {
+			if (le32_to_cpu(sbi->ckpt->cur_node_segno[0]) == segno ||
+				le32_to_cpu(sbi->ckpt->cur_data_segno[0]) == segno ||
+				le32_to_cpu(sbi->ckpt->cur_node_segno[1]) == segno ||
+				le32_to_cpu(sbi->ckpt->cur_data_segno[1]) == segno ||
+				le32_to_cpu(sbi->ckpt->cur_node_segno[2]) == segno ||
+				le32_to_cpu(sbi->ckpt->cur_data_segno[2]) == segno) {
 				continue;
 			} else {
 				free_segs++;
@@ -1042,9 +1452,7 @@
 	char *ptr = NULL;
 
 	/* remove sit journal */
-	sum->n_sits = 0;
-
-	fsck->chk.free_segs = 0;
+	sum->journal.n_sits = 0;
 
 	ptr = fsck->main_area_bitmap;
 
@@ -1065,6 +1473,8 @@
 			valid_blocks += get_bits_in_byte(sit->valid_map[i]);
 
 		se = get_seg_entry(sbi, segno);
+		memcpy(se->cur_valid_map, ptr, SIT_VBLOCK_MAP_SIZE);
+		se->valid_blocks = valid_blocks;
 		type = se->type;
 		if (type >= NO_CHECK_TYPE) {
 			ASSERT_MSG("Invalide type and valid blocks=%x,%x",
@@ -1076,29 +1486,255 @@
 		rewrite_current_sit_page(sbi, segno, sit_blk);
 		free(sit_blk);
 
-		if (valid_blocks == 0 &&
-				sbi->ckpt->cur_node_segno[0] != segno &&
-				sbi->ckpt->cur_data_segno[0] != segno &&
-				sbi->ckpt->cur_node_segno[1] != segno &&
-				sbi->ckpt->cur_data_segno[1] != segno &&
-				sbi->ckpt->cur_node_segno[2] != segno &&
-				sbi->ckpt->cur_data_segno[2] != segno)
-			fsck->chk.free_segs++;
-
 		ptr += SIT_VBLOCK_MAP_SIZE;
 	}
 }
 
+static int flush_sit_journal_entries(struct f2fs_sb_info *sbi)
+{
+	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
+	struct f2fs_journal *journal = &curseg->sum_blk->journal;
+	struct sit_info *sit_i = SIT_I(sbi);
+	unsigned int segno;
+	int i;
+
+	for (i = 0; i < sits_in_cursum(journal); i++) {
+		struct f2fs_sit_block *sit_blk;
+		struct f2fs_sit_entry *sit;
+		struct seg_entry *se;
+
+		segno = segno_in_journal(journal, i);
+		se = get_seg_entry(sbi, segno);
+
+		sit_blk = get_current_sit_page(sbi, segno);
+		sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)];
+
+		memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
+		sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+							se->valid_blocks);
+		sit->mtime = cpu_to_le64(se->mtime);
+
+		rewrite_current_sit_page(sbi, segno, sit_blk);
+		free(sit_blk);
+	}
+
+	journal->n_sits = 0;
+	return i;
+}
+
+static int flush_nat_journal_entries(struct f2fs_sb_info *sbi)
+{
+	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
+	struct f2fs_journal *journal = &curseg->sum_blk->journal;
+	struct f2fs_nat_block *nat_block;
+	pgoff_t block_addr;
+	int entry_off;
+	nid_t nid;
+	int ret;
+	int i = 0;
+
+	nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+	ASSERT(nat_block);
+next:
+	if (i >= nats_in_cursum(journal)) {
+		free(nat_block);
+		journal->n_nats = 0;
+		return i;
+	}
+
+	nid = le32_to_cpu(nid_in_journal(journal, i));
+
+	entry_off = nid % NAT_ENTRY_PER_BLOCK;
+	block_addr = current_nat_addr(sbi, nid);
+
+	ret = dev_read_block(nat_block, block_addr);
+	ASSERT(ret >= 0);
+
+	memcpy(&nat_block->entries[entry_off], &nat_in_journal(journal, i),
+					sizeof(struct f2fs_nat_entry));
+
+	ret = dev_write_block(nat_block, block_addr);
+	ASSERT(ret >= 0);
+	i++;
+	goto next;
+}
+
+void flush_journal_entries(struct f2fs_sb_info *sbi)
+{
+	int n_nats = flush_nat_journal_entries(sbi);
+	int n_sits = flush_sit_journal_entries(sbi);
+
+	if (n_nats || n_sits)
+		write_checkpoint(sbi);
+}
+
+void flush_sit_entries(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	struct sit_info *sit_i = SIT_I(sbi);
+	unsigned int segno = 0;
+	u32 free_segs = 0;
+
+	/* update free segments */
+	for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
+		struct f2fs_sit_block *sit_blk;
+		struct f2fs_sit_entry *sit;
+		struct seg_entry *se;
+
+		se = get_seg_entry(sbi, segno);
+
+		if (!se->dirty)
+			continue;
+
+		sit_blk = get_current_sit_page(sbi, segno);
+		sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)];
+		memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
+		sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+							se->valid_blocks);
+		rewrite_current_sit_page(sbi, segno, sit_blk);
+		free(sit_blk);
+
+		if (se->valid_blocks == 0x0 &&
+				!IS_CUR_SEGNO(sbi, segno, NO_CHECK_TYPE))
+			free_segs++;
+	}
+
+	set_cp(free_segment_count, free_segs);
+}
+
+int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left, int type)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct seg_entry *se;
+	u32 segno;
+	u64 offset;
+	int not_enough = 0;
+	u64 end_blkaddr = (get_sb(segment_count_main) <<
+			get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr);
+
+	if (get_free_segments(sbi) <= SM_I(sbi)->reserved_segments + 1)
+		not_enough = 1;
+
+	while (*to >= SM_I(sbi)->main_blkaddr && *to < end_blkaddr) {
+		segno = GET_SEGNO(sbi, *to);
+		offset = OFFSET_IN_SEG(sbi, *to);
+
+		se = get_seg_entry(sbi, segno);
+
+		if (se->valid_blocks == sbi->blocks_per_seg ||
+				IS_CUR_SEGNO(sbi, segno, type)) {
+			*to = left ? START_BLOCK(sbi, segno) - 1:
+						START_BLOCK(sbi, segno + 1);
+			continue;
+		}
+
+		if (se->valid_blocks == 0 && not_enough) {
+			*to = left ? START_BLOCK(sbi, segno) - 1:
+						START_BLOCK(sbi, segno + 1);
+			continue;
+		}
+
+		if (se->valid_blocks == 0 && !(segno % sbi->segs_per_sec)) {
+			struct seg_entry *se2;
+			unsigned int i;
+
+			for (i = 1; i < sbi->segs_per_sec; i++) {
+				se2 = get_seg_entry(sbi, segno + i);
+				if (se2->valid_blocks)
+					break;
+			}
+			if (i == sbi->segs_per_sec)
+				return 0;
+		}
+
+		if (se->type == type &&
+			!f2fs_test_bit(offset, (const char *)se->cur_valid_map))
+			return 0;
+
+		*to = left ? *to - 1: *to + 1;
+	}
+	return -1;
+}
+
+void move_curseg_info(struct f2fs_sb_info *sbi, u64 from)
+{
+	int i, ret;
+
+	/* update summary blocks having nullified journal entries */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+		struct f2fs_summary_block buf;
+		u32 old_segno;
+		u64 ssa_blk, to;
+
+		/* update original SSA too */
+		ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+		ret = dev_write_block(curseg->sum_blk, ssa_blk);
+		ASSERT(ret >= 0);
+
+		to = from;
+		ret = find_next_free_block(sbi, &to, 0, i);
+		ASSERT(ret == 0);
+
+		old_segno = curseg->segno;
+		curseg->segno = GET_SEGNO(sbi, to);
+		curseg->next_blkoff = OFFSET_IN_SEG(sbi, to);
+		curseg->alloc_type = SSR;
+
+		/* update new segno */
+		ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+		ret = dev_read_block(&buf, ssa_blk);
+		ASSERT(ret >= 0);
+
+		memcpy(curseg->sum_blk, &buf, SUM_ENTRIES_SIZE);
+
+		/* update se->types */
+		reset_curseg(sbi, i);
+
+		DBG(1, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
+				i, old_segno, curseg->segno, from);
+	}
+}
+
+void zero_journal_entries(struct f2fs_sb_info *sbi)
+{
+	int i;
+
+	for (i = 0; i < NO_CHECK_TYPE; i++)
+		CURSEG_I(sbi, i)->sum_blk->journal.n_nats = 0;
+}
+
+void write_curseg_info(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	int i;
+
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		cp->alloc_type[i] = CURSEG_I(sbi, i)->alloc_type;
+		if (i < CURSEG_HOT_NODE) {
+			set_cp(cur_data_segno[i], CURSEG_I(sbi, i)->segno);
+			set_cp(cur_data_blkoff[i],
+					CURSEG_I(sbi, i)->next_blkoff);
+		} else {
+			int n = i - CURSEG_HOT_NODE;
+
+			set_cp(cur_node_segno[n], CURSEG_I(sbi, i)->segno);
+			set_cp(cur_node_blkoff[n],
+					CURSEG_I(sbi, i)->next_blkoff);
+		}
+	}
+}
+
 int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid,
 					struct f2fs_nat_entry *raw_nat)
 {
 	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
-	struct f2fs_summary_block *sum = curseg->sum_blk;
+	struct f2fs_journal *journal = &curseg->sum_blk->journal;
 	int i = 0;
 
-	for (i = 0; i < nats_in_cursum(sum); i++) {
-		if (le32_to_cpu(nid_in_journal(sum, i)) == nid) {
-			memcpy(raw_nat, &nat_in_journal(sum, i),
+	for (i = 0; i < nats_in_cursum(journal); i++) {
+		if (le32_to_cpu(nid_in_journal(journal, i)) == nid) {
+			memcpy(raw_nat, &nat_in_journal(journal, i),
 						sizeof(struct f2fs_nat_entry));
 			DBG(3, "==> Found nid [0x%x] in nat cache\n", nid);
 			return i;
@@ -1110,36 +1746,27 @@
 void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid)
 {
 	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
-	struct f2fs_summary_block *sum = curseg->sum_blk;
-	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	struct f2fs_journal *journal = &curseg->sum_blk->journal;
 	struct f2fs_nat_block *nat_block;
-	pgoff_t block_off;
 	pgoff_t block_addr;
-	int seg_off, entry_off;
+	int entry_off;
 	int ret;
 	int i = 0;
 
 	/* check in journal */
-	for (i = 0; i < nats_in_cursum(sum); i++) {
-		if (le32_to_cpu(nid_in_journal(sum, i)) == nid) {
-			memset(&nat_in_journal(sum, i), 0,
+	for (i = 0; i < nats_in_cursum(journal); i++) {
+		if (le32_to_cpu(nid_in_journal(journal, i)) == nid) {
+			memset(&nat_in_journal(journal, i), 0,
 					sizeof(struct f2fs_nat_entry));
 			FIX_MSG("Remove nid [0x%x] in nat journal\n", nid);
 			return;
 		}
 	}
 	nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+	ASSERT(nat_block);
 
-	block_off = nid / NAT_ENTRY_PER_BLOCK;
 	entry_off = nid % NAT_ENTRY_PER_BLOCK;
-
-	seg_off = block_off >> sbi->log_blocks_per_seg;
-	block_addr = (pgoff_t)(nm_i->nat_blkaddr +
-			(seg_off << sbi->log_blocks_per_seg << 1) +
-			(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
-
-	if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
-		block_addr += sbi->blocks_per_seg;
+	block_addr = current_nat_addr(sbi, nid);
 
 	ret = dev_read_block(nat_block, block_addr);
 	ASSERT(ret >= 0);
@@ -1152,12 +1779,71 @@
 	free(nat_block);
 }
 
+void write_checkpoint(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	block_t orphan_blks = 0;
+	unsigned long long cp_blk_no;
+	u32 flags = CP_UMOUNT_FLAG;
+	int i, ret;
+	u_int32_t crc = 0;
+
+	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) {
+		orphan_blks = __start_sum_addr(sbi) - 1;
+		flags |= CP_ORPHAN_PRESENT_FLAG;
+	}
+
+	set_cp(ckpt_flags, flags);
+
+	set_cp(free_segment_count, get_free_segments(sbi));
+	set_cp(valid_block_count, sbi->total_valid_block_count);
+	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload));
+
+	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
+	*((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+
+	cp_blk_no = get_sb(cp_blkaddr);
+	if (sbi->cur_cp == 2)
+		cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+
+	/* write the first cp */
+	ret = dev_write_block(cp, cp_blk_no++);
+	ASSERT(ret >= 0);
+
+	/* skip payload */
+	cp_blk_no += get_sb(cp_payload);
+	/* skip orphan blocks */
+	cp_blk_no += orphan_blks;
+
+	/* update summary blocks having nullified journal entries */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+		u64 ssa_blk;
+
+		ret = dev_write_block(curseg->sum_blk, cp_blk_no++);
+		ASSERT(ret >= 0);
+
+		/* update original SSA too */
+		ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+		ret = dev_write_block(curseg->sum_blk, ssa_blk);
+		ASSERT(ret >= 0);
+	}
+
+	/* write the last cp */
+	ret = dev_write_block(cp, cp_blk_no++);
+	ASSERT(ret >= 0);
+}
+
 void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
 {
+	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
+	struct f2fs_journal *journal = &curseg->sum_blk->journal;
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
-	struct f2fs_super_block *raw_sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct f2fs_nm_info *nm_i = NM_I(sbi);
 	struct f2fs_nat_block *nat_block;
+	struct node_info ni;
 	u32 nid, nr_nat_blks;
 	pgoff_t block_off;
 	pgoff_t block_addr;
@@ -1169,13 +1855,17 @@
 	ASSERT(nat_block);
 
 	/* Alloc & build nat entry bitmap */
-	nr_nat_blks = (le32_to_cpu(raw_sb->segment_count_nat) / 2) <<
-						sbi->log_blocks_per_seg;
+	nr_nat_blks = (get_sb(segment_count_nat) / 2) <<
+					sbi->log_blocks_per_seg;
 
 	fsck->nr_nat_entries = nr_nat_blks * NAT_ENTRY_PER_BLOCK;
 	fsck->nat_area_bitmap_sz = (fsck->nr_nat_entries + 7) / 8;
 	fsck->nat_area_bitmap = calloc(fsck->nat_area_bitmap_sz, 1);
-	ASSERT(fsck->nat_area_bitmap != NULL);
+	ASSERT(fsck->nat_area_bitmap);
+
+	fsck->entries = calloc(sizeof(struct f2fs_nat_entry),
+					fsck->nr_nat_entries);
+	ASSERT(fsck->entries);
 
 	for (block_off = 0; block_off < nr_nat_blks; block_off++) {
 
@@ -1192,40 +1882,88 @@
 
 		nid = block_off * NAT_ENTRY_PER_BLOCK;
 		for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) {
-			struct f2fs_nat_entry raw_nat;
-			struct node_info ni;
 			ni.nid = nid + i;
 
 			if ((nid + i) == F2FS_NODE_INO(sbi) ||
 					(nid + i) == F2FS_META_INO(sbi)) {
-				ASSERT(nat_block->entries[i].block_addr != 0x0);
+				/* block_addr of node/meta inode should be 0x1 */
+				if (le32_to_cpu(nat_block->entries[i].block_addr) != 0x1) {
+					FIX_MSG("ino: 0x%x node/meta inode, block_addr= 0x%x -> 0x1",
+							nid + i, le32_to_cpu(nat_block->entries[i].block_addr));
+					nat_block->entries[i].block_addr = cpu_to_le32(0x1);
+					ret = dev_write_block(nat_block, block_addr);
+					ASSERT(ret >= 0);
+				}
 				continue;
 			}
 
-			if (lookup_nat_in_journal(sbi, nid + i,
-							&raw_nat) >= 0) {
-				node_info_from_raw_nat(&ni, &raw_nat);
-				if (ni.blk_addr != 0x0) {
-					f2fs_set_bit(nid + i,
-							fsck->nat_area_bitmap);
-					fsck->chk.valid_nat_entry_cnt++;
-					DBG(3, "nid[0x%x] in nat cache\n",
-								nid + i);
-				}
-			} else {
-				node_info_from_raw_nat(&ni,
-						&nat_block->entries[i]);
-				if (ni.blk_addr == 0)
-					continue;
-				ASSERT(nid + i != 0x0);
-
-				DBG(3, "nid[0x%8x] addr[0x%16x] ino[0x%8x]\n",
-					nid + i, ni.blk_addr, ni.ino);
-				f2fs_set_bit(nid + i, fsck->nat_area_bitmap);
-				fsck->chk.valid_nat_entry_cnt++;
+			node_info_from_raw_nat(&ni, &nat_block->entries[i]);
+			if (ni.blk_addr == 0x0)
+				continue;
+			if (ni.ino == 0x0) {
+				ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]"
+					" is invalid\n", ni.ino, ni.blk_addr);
 			}
+			if (ni.ino == (nid + i)) {
+				fsck->nat_valid_inode_cnt++;
+				DBG(3, "ino[0x%8x] maybe is inode\n", ni.ino);
+			}
+			if (nid + i == 0) {
+				/*
+				 * nat entry [0] must be null.  If
+				 * it is corrupted, set its bit in
+				 * nat_area_bitmap, fsck_verify will
+				 * nullify it
+				 */
+				ASSERT_MSG("Invalid nat entry[0]: "
+					"blk_addr[0x%x]\n", ni.blk_addr);
+				c.fix_on = 1;
+				fsck->chk.valid_nat_entry_cnt--;
+			}
+
+			DBG(3, "nid[0x%8x] addr[0x%16x] ino[0x%8x]\n",
+				nid + i, ni.blk_addr, ni.ino);
+			f2fs_set_bit(nid + i, fsck->nat_area_bitmap);
+			fsck->chk.valid_nat_entry_cnt++;
+
+			fsck->entries[nid + i] = nat_block->entries[i];
 		}
 	}
+
+	/* Traverse nat journal, update the corresponding entries */
+	for (i = 0; i < nats_in_cursum(journal); i++) {
+		struct f2fs_nat_entry raw_nat;
+		nid = le32_to_cpu(nid_in_journal(journal, i));
+		ni.nid = nid;
+
+		DBG(3, "==> Found nid [0x%x] in nat cache, update it\n", nid);
+
+		/* Clear the original bit and count */
+		if (fsck->entries[nid].block_addr != 0x0) {
+			fsck->chk.valid_nat_entry_cnt--;
+			f2fs_clear_bit(nid, fsck->nat_area_bitmap);
+			if (fsck->entries[nid].ino == nid)
+				fsck->nat_valid_inode_cnt--;
+		}
+
+		/* Use nat entries in journal */
+		memcpy(&raw_nat, &nat_in_journal(journal, i),
+					sizeof(struct f2fs_nat_entry));
+		node_info_from_raw_nat(&ni, &raw_nat);
+		if (ni.blk_addr != 0x0) {
+			if (ni.ino == 0x0)
+				ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]"
+					" is invalid\n", ni.ino, ni.blk_addr);
+			if (ni.ino == nid) {
+				fsck->nat_valid_inode_cnt++;
+				DBG(3, "ino[0x%8x] maybe is inode\n", ni.ino);
+			}
+			f2fs_set_bit(nid, fsck->nat_area_bitmap);
+			fsck->chk.valid_nat_entry_cnt++;
+			DBG(3, "nid[0x%x] in nat cache\n", nid);
+		}
+		fsck->entries[nid] = raw_nat;
+	}
 	free(nat_block);
 
 	DBG(1, "valid nat entries (block_addr != 0x0) [0x%8x : %u]\n",
@@ -1233,8 +1971,43 @@
 			fsck->chk.valid_nat_entry_cnt);
 }
 
+static int check_sector_size(struct f2fs_super_block *sb)
+{
+	int index;
+	u_int32_t log_sectorsize, log_sectors_per_block;
+	u_int8_t *zero_buff;
+
+	log_sectorsize = log_base_2(c.sector_size);
+	log_sectors_per_block = log_base_2(c.sectors_per_blk);
+
+	if (log_sectorsize == get_sb(log_sectorsize) &&
+			log_sectors_per_block == get_sb(log_sectors_per_block))
+		return 0;
+
+	zero_buff = calloc(F2FS_BLKSIZE, 1);
+	ASSERT(zero_buff);
+
+	set_sb(log_sectorsize, log_sectorsize);
+	set_sb(log_sectors_per_block, log_sectors_per_block);
+
+	memcpy(zero_buff + F2FS_SUPER_OFFSET, sb, sizeof(*sb));
+	DBG(1, "\tWriting super block, at offset 0x%08x\n", 0);
+	for (index = 0; index < 2; index++) {
+		if (dev_write(zero_buff, index * F2FS_BLKSIZE, F2FS_BLKSIZE)) {
+			MSG(1, "\tError: Failed while writing supe_blk "
+				"on disk!!! index : %d\n", index);
+			free(zero_buff);
+			return -1;
+		}
+	}
+
+	free(zero_buff);
+	return 0;
+}
+
 int f2fs_do_mount(struct f2fs_sb_info *sbi)
 {
+	struct f2fs_checkpoint *cp = NULL;
 	int ret;
 
 	sbi->active_logs = NR_CURSEG_TYPE;
@@ -1245,7 +2018,11 @@
 			return -1;
 	}
 
-	print_raw_sb_info(sbi);
+	ret = check_sector_size(sbi->raw_super);
+	if (ret)
+		return -1;
+
+	print_raw_sb_info(F2FS_RAW_SUPER(sbi));
 
 	init_sb_info(sbi);
 
@@ -1259,26 +2036,25 @@
 		ERR_MSG("Checkpoint is polluted\n");
 		return -1;
 	}
+	cp = F2FS_CKPT(sbi);
 
 	print_ckpt_info(sbi);
 
-	if (config.auto_fix) {
-		u32 flag = le32_to_cpu(sbi->ckpt->ckpt_flags);
+	if (c.auto_fix || c.preen_mode) {
+		u32 flag = get_cp(ckpt_flags);
 
 		if (flag & CP_FSCK_FLAG)
-			config.fix_on = 1;
-		else
+			c.fix_on = 1;
+		else if (!c.preen_mode)
 			return 1;
 	}
 
-	config.bug_on = 0;
+	c.bug_on = 0;
 
-	sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count);
-	sbi->total_valid_inode_count =
-			le32_to_cpu(sbi->ckpt->valid_inode_count);
-	sbi->user_block_count = le64_to_cpu(sbi->ckpt->user_block_count);
-	sbi->total_valid_block_count =
-			le64_to_cpu(sbi->ckpt->valid_block_count);
+	sbi->total_valid_node_count = get_cp(valid_node_count);
+	sbi->total_valid_inode_count = get_cp(valid_inode_count);
+	sbi->user_block_count = get_cp(user_block_count);
+	sbi->total_valid_block_count = get_cp(valid_block_count);
 	sbi->last_valid_block_count = sbi->total_valid_block_count;
 	sbi->alloc_valid_block_count = 0;
 
@@ -1288,7 +2064,7 @@
 	}
 
 	if (build_node_manager(sbi)) {
-		ERR_MSG("build_segment_manager failed\n");
+		ERR_MSG("build_node_manager failed\n");
 		return -1;
 	}
 
@@ -1303,6 +2079,8 @@
 	unsigned int i;
 
 	/* free nm_info */
+	if (c.func == SLOAD)
+		free(nm_i->nid_bitmap);
 	free(nm_i->nat_bitmap);
 	free(sbi->nm_info);
 
diff --git a/fsck/node.c b/fsck/node.c
new file mode 100644
index 0000000..fe923e5
--- /dev/null
+++ b/fsck/node.c
@@ -0,0 +1,250 @@
+/**
+ * node.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "fsck.h"
+#include "node.h"
+
+void f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid, int inode)
+{
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	nid_t i, inode_cnt, node_cnt;
+
+	for (i = 0; i < nm_i->max_nid; i++)
+		if(f2fs_test_bit(i, nm_i->nid_bitmap) == 0)
+			break;
+
+	ASSERT(i < nm_i->max_nid);
+	f2fs_set_bit(i, nm_i->nid_bitmap);
+	*nid = i;
+
+	inode_cnt = get_cp(valid_inode_count);
+	node_cnt = get_cp(valid_node_count);
+	if (inode)
+		set_cp(valid_inode_count, inode_cnt + 1);
+	set_cp(valid_node_count, node_cnt + 1);
+}
+
+void set_data_blkaddr(struct dnode_of_data *dn)
+{
+	__le32 *addr_array;
+	struct f2fs_node *node_blk = dn->node_blk;
+	unsigned int ofs_in_node = dn->ofs_in_node;
+
+	addr_array = blkaddr_in_node(node_blk);
+	addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr);
+	if (dn->node_blk != dn->inode_blk)
+		dn->ndirty = 1;
+	else
+		dn->idirty = 1;
+}
+
+/*
+ * In this function, we get a new node blk, and write back
+ * node_blk would be sloadd in RAM, linked by dn->node_blk
+ */
+block_t new_node_block(struct f2fs_sb_info *sbi,
+				struct dnode_of_data *dn, unsigned int ofs)
+{
+	struct f2fs_node *f2fs_inode;
+	struct f2fs_node *node_blk;
+	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_summary sum;
+	struct node_info ni;
+	block_t blkaddr;
+	int type;
+
+	f2fs_inode = dn->inode_blk;
+
+	node_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(node_blk);
+
+	node_blk->footer.nid = cpu_to_le32(dn->nid);
+	node_blk->footer.ino = f2fs_inode->footer.ino;
+	node_blk->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT);
+	node_blk->footer.cp_ver = ckpt->checkpoint_ver;
+
+	type = CURSEG_COLD_NODE;
+	if (IS_DNODE(node_blk)) {
+		if (S_ISDIR(le16_to_cpu(f2fs_inode->i.i_mode)))
+			type = CURSEG_HOT_NODE;
+		else
+			type = CURSEG_WARM_NODE;
+	}
+
+	get_node_info(sbi, dn->nid, &ni);
+	set_summary(&sum, dn->nid, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, type);
+
+	/* update nat info */
+	update_nat_blkaddr(sbi, le32_to_cpu(f2fs_inode->footer.ino),
+						dn->nid, blkaddr);
+
+	dn->node_blk = node_blk;
+	inc_inode_blocks(dn);
+	return blkaddr;
+}
+
+/*
+ * get_node_path - Get the index path of pgoff_t block
+ * @offset: offset in the current index node block.
+ * @noffset: NO. of the index block within a file.
+ * return: depth of the index path.
+ *
+ * By default, it sets inline_xattr and inline_data
+ */
+static int get_node_path(unsigned long block,
+				int offset[4], unsigned int noffset[4])
+{
+	const long direct_index = DEF_ADDRS_PER_INODE_INLINE_XATTR;
+	const long direct_blks = ADDRS_PER_BLOCK;
+	const long dptrs_per_blk = NIDS_PER_BLOCK;
+	const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
+	const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK;
+	int n = 0;
+	int level = 0;
+
+	noffset[0] = 0;
+	if (block < direct_index) {
+		offset[n] = block;
+		goto got;
+	}
+
+	block -= direct_index;
+	if (block < direct_blks) {
+		offset[n++] = NODE_DIR1_BLOCK;
+		noffset[n]= 1;
+		offset[n] = block;
+		level = 1;
+		goto got;
+	}
+	block -= direct_blks;
+	if (block < direct_blks) {
+		offset[n++] = NODE_DIR2_BLOCK;
+		noffset[n] = 2;
+		offset[n] = block;
+		level = 1;
+		goto got;
+	}
+	block -= direct_blks;
+	if (block < indirect_blks) {
+		offset[n++] = NODE_IND1_BLOCK;
+		noffset[n] = 3;
+		offset[n++] = block / direct_blks;
+		noffset[n] = 4 + offset[n - 1];
+		offset[n] = block % direct_blks;
+		level = 2;
+		goto got;
+	}
+	block -= indirect_blks;
+	if (block < indirect_blks) {
+		offset[n++] = NODE_IND2_BLOCK;
+		noffset[n] = 4 + dptrs_per_blk;
+		offset[n++] = block / direct_blks;
+		noffset[n] = 5 + dptrs_per_blk + offset[n - 1];
+		offset[n] = block % direct_blks;
+		level = 2;
+		goto got;
+	}
+	block -= indirect_blks;
+	if (block < dindirect_blks) {
+		offset[n++] = NODE_DIND_BLOCK;
+		noffset[n] = 5 + (dptrs_per_blk * 2);
+		offset[n++] = block / indirect_blks;
+		noffset[n] = 6 + (dptrs_per_blk * 2) +
+			offset[n - 1] * (dptrs_per_blk + 1);
+		offset[n++] = (block / direct_blks) % dptrs_per_blk;
+		noffset[n] = 7 + (dptrs_per_blk * 2) +
+			offset[n - 2] * (dptrs_per_blk + 1) +
+			offset[n - 1];
+		offset[n] = block % direct_blks;
+		level = 3;
+		goto got;
+	} else {
+		ASSERT(0);
+	}
+got:
+	return level;
+}
+
+void get_dnode_of_data(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
+						pgoff_t index, int mode)
+{
+	int offset[4];
+	unsigned int noffset[4];
+	struct f2fs_node *parent = NULL;
+	nid_t nids[4];
+	block_t nblk[4];
+	struct node_info ni;
+	int level, i;
+	int ret;
+
+	level = get_node_path(index, offset, noffset);
+
+	nids[0] = dn->nid;
+	parent = dn->inode_blk;
+	if (level != 0)
+		nids[1] = get_nid(parent, offset[0], 1);
+	else
+		dn->node_blk = dn->inode_blk;
+
+	get_node_info(sbi, nids[0], &ni);
+	nblk[0] = ni.blk_addr;
+
+	for (i = 1; i <= level; i++) {
+		if (!nids[i] && mode == ALLOC_NODE) {
+			f2fs_alloc_nid(sbi, &nids[i], 0);
+
+			dn->nid = nids[i];
+
+			/* Function new_node_blk get a new f2fs_node blk and update*/
+			/* We should make sure that dn->node_blk == NULL*/
+			nblk[i] = new_node_block(sbi, dn, noffset[i]);
+			ASSERT(nblk[i]);
+
+			set_nid(parent, offset[i - 1], nids[i], i == 1);
+		} else {
+			/* If Sparse file no read API, */
+			struct node_info ni;
+
+			get_node_info(sbi, nids[i], &ni);
+			dn->node_blk = calloc(BLOCK_SZ, 1);
+			ASSERT(dn->node_blk);
+
+			ret = dev_read_block(dn->node_blk, ni.blk_addr);
+			ASSERT(ret >= 0);
+
+			nblk[i] = ni.blk_addr;
+		}
+
+		if (mode == ALLOC_NODE){
+			/* Parent node may have changed */
+			ret = dev_write_block(parent, nblk[i - 1]);
+			ASSERT(ret >= 0);
+		}
+		if (i != 1)
+			free(parent);
+
+		if (i < level) {
+			parent = dn->node_blk;
+			nids[i + 1] = get_nid(parent, offset[i], 0);
+		}
+	}
+
+	dn->nid = nids[level];
+	dn->ofs_in_node = offset[level];
+	dn->data_blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
+	dn->node_blkaddr = nblk[level];
+}
diff --git a/fsck/node.h b/fsck/node.h
new file mode 100644
index 0000000..721e5b7
--- /dev/null
+++ b/fsck/node.h
@@ -0,0 +1,101 @@
+/**
+ * node.h
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _NODE_H_
+#define _NODE_H_
+
+#include "fsck.h"
+
+#define ADDRS_PER_PAGE(page) \
+	(IS_INODE(page) ? ADDRS_PER_INODE(&page->i) : ADDRS_PER_BLOCK)
+
+static inline int IS_INODE(struct f2fs_node *node)
+{
+	return ((node)->footer.nid == (node)->footer.ino);
+}
+
+static inline __le32 *blkaddr_in_node(struct f2fs_node *node)
+{
+	return IS_INODE(node) ? node->i.i_addr : node->dn.addr;
+}
+
+static inline block_t datablock_addr(struct f2fs_node *node_page,
+					unsigned int offset)
+{
+	__le32 *addr_array;
+
+	ASSERT(node_page);
+	addr_array = blkaddr_in_node(node_page);
+	return le32_to_cpu(addr_array[offset]);
+}
+
+static inline void set_nid(struct f2fs_node * rn, int off, nid_t nid, int i)
+{
+	if (i)
+		rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
+	else
+		rn->in.nid[off] = cpu_to_le32(nid);
+}
+
+static inline nid_t get_nid(struct f2fs_node * rn, int off, int i)
+{
+	if (i)
+		return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]);
+	else
+		return le32_to_cpu(rn->in.nid[off]);
+}
+
+enum {
+	ALLOC_NODE,	/* allocate a new node page if needed */
+	LOOKUP_NODE,	/* lookup up a node without readahead */
+	LOOKUP_NODE_RA,
+};
+
+static inline void set_new_dnode(struct dnode_of_data *dn,
+		struct f2fs_node *iblk, struct f2fs_node *nblk, nid_t nid)
+{
+	memset(dn, 0, sizeof(*dn));
+	dn->inode_blk = iblk;
+	dn->node_blk = nblk;
+	dn->nid = nid;
+	dn->idirty = 0;
+	dn->ndirty = 0;
+}
+
+static inline void inc_inode_blocks(struct dnode_of_data *dn)
+{
+	u64 blocks = le64_to_cpu(dn->inode_blk->i.i_blocks);
+
+	dn->inode_blk->i.i_blocks = cpu_to_le64(blocks + 1);
+	dn->idirty = 1;
+}
+
+static inline int IS_DNODE(struct f2fs_node *node_page)
+{
+	unsigned int ofs = ofs_of_node(node_page);
+
+	if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK ||
+			ofs == 5 + 2 * NIDS_PER_BLOCK)
+		return 0;
+
+	if (ofs >= 6 + 2 * NIDS_PER_BLOCK) {
+		ofs -= 6 + 2 * NIDS_PER_BLOCK;
+		if (!((long int)ofs % (NIDS_PER_BLOCK + 1)))
+			return 0;
+	}
+	return 1;
+}
+
+#endif
diff --git a/fsck/resize.c b/fsck/resize.c
new file mode 100644
index 0000000..29cb1c0
--- /dev/null
+++ b/fsck/resize.c
@@ -0,0 +1,601 @@
+/**
+ * resize.c
+ *
+ * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "fsck.h"
+
+static int get_new_sb(struct f2fs_super_block *sb)
+{
+	u_int32_t zone_size_bytes, zone_align_start_offset;
+	u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
+	u_int32_t sit_segments, diff, total_meta_segments;
+	u_int32_t total_valid_blks_available;
+	u_int32_t sit_bitmap_size, max_sit_bitmap_size;
+	u_int32_t max_nat_bitmap_size, max_nat_segments;
+	u_int32_t segment_size_bytes = 1 << (get_sb(log_blocksize) +
+					get_sb(log_blocks_per_seg));
+	u_int32_t blks_per_seg = 1 << get_sb(log_blocks_per_seg);
+	u_int32_t segs_per_zone = get_sb(segs_per_sec) * get_sb(secs_per_zone);
+
+	set_sb(block_count, c.target_sectors >>
+				get_sb(log_sectors_per_block));
+
+	zone_size_bytes = segment_size_bytes * segs_per_zone;
+	zone_align_start_offset =
+		(c.start_sector * c.sector_size +
+		2 * F2FS_BLKSIZE + zone_size_bytes - 1) /
+		zone_size_bytes * zone_size_bytes -
+		c.start_sector * c.sector_size;
+
+	set_sb(segment_count, (c.target_sectors * c.sector_size -
+				zone_align_start_offset) / segment_size_bytes /
+				c.segs_per_sec * c.segs_per_sec);
+
+	blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
+	sit_segments = SEG_ALIGN(blocks_for_sit);
+	set_sb(segment_count_sit, sit_segments * 2);
+	set_sb(nat_blkaddr, get_sb(sit_blkaddr) +
+				get_sb(segment_count_sit) * blks_per_seg);
+
+	total_valid_blks_available = (get_sb(segment_count) -
+			(get_sb(segment_count_ckpt) +
+			get_sb(segment_count_sit))) * blks_per_seg;
+	blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
+	set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
+
+	sit_bitmap_size = ((get_sb(segment_count_sit) / 2) <<
+				get_sb(log_blocks_per_seg)) / 8;
+	if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE)
+		max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE;
+	else
+		max_sit_bitmap_size = sit_bitmap_size;
+
+	/*
+	 * It should be reserved minimum 1 segment for nat.
+	 * When sit is too large, we should expand cp area. It requires more pages for cp.
+	 */
+	if (max_sit_bitmap_size >
+			(CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) {
+		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1;
+		set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size));
+	} else {
+		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
+			- max_sit_bitmap_size;
+		set_sb(cp_payload, 0);
+	}
+
+	max_nat_segments = (max_nat_bitmap_size * 8) >>
+					get_sb(log_blocks_per_seg);
+
+	if (get_sb(segment_count_nat) > max_nat_segments)
+		set_sb(segment_count_nat, max_nat_segments);
+
+	set_sb(segment_count_nat, get_sb(segment_count_nat) * 2);
+
+	set_sb(ssa_blkaddr, get_sb(nat_blkaddr) +
+				get_sb(segment_count_nat) * blks_per_seg);
+
+	total_valid_blks_available = (get_sb(segment_count) -
+			(get_sb(segment_count_ckpt) +
+			get_sb(segment_count_sit) +
+			get_sb(segment_count_nat))) * blks_per_seg;
+
+	blocks_for_ssa = total_valid_blks_available / blks_per_seg + 1;
+
+	set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
+
+	total_meta_segments = get_sb(segment_count_ckpt) +
+		get_sb(segment_count_sit) +
+		get_sb(segment_count_nat) +
+		get_sb(segment_count_ssa);
+
+	diff = total_meta_segments % segs_per_zone;
+	if (diff)
+		set_sb(segment_count_ssa, get_sb(segment_count_ssa) +
+			(segs_per_zone - diff));
+
+	set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) *
+			 blks_per_seg);
+
+	set_sb(segment_count_main, get_sb(segment_count) -
+			(get_sb(segment_count_ckpt) +
+			 get_sb(segment_count_sit) +
+			 get_sb(segment_count_nat) +
+			 get_sb(segment_count_ssa)));
+
+	set_sb(section_count, get_sb(segment_count_main) /
+						get_sb(segs_per_sec));
+
+	set_sb(segment_count_main, get_sb(section_count) *
+						get_sb(segs_per_sec));
+
+	/* Let's determine the best reserved and overprovisioned space */
+	c.new_overprovision = get_best_overprovision(sb);
+	c.new_reserved_segments =
+		(2 * (100 / c.new_overprovision + 1) + 6) *
+						get_sb(segs_per_sec);
+
+	if ((get_sb(segment_count_main) - 2) < c.new_reserved_segments ||
+		get_sb(segment_count_main) * blks_per_seg >
+						get_sb(block_count)) {
+		MSG(0, "\tError: Device size is not sufficient for F2FS volume, "
+			"more segment needed =%u",
+			c.new_reserved_segments -
+			(get_sb(segment_count_main) - 2));
+		return -1;
+	}
+	return 0;
+}
+
+static void migrate_main(struct f2fs_sb_info *sbi, unsigned int offset)
+{
+	void *raw = calloc(BLOCK_SZ, 1);
+	struct seg_entry *se;
+	block_t from, to;
+	int i, j, ret;
+	struct f2fs_summary sum;
+
+	ASSERT(raw != NULL);
+
+	for (i = TOTAL_SEGS(sbi) - 1; i >= 0; i--) {
+		se = get_seg_entry(sbi, i);
+		if (!se->valid_blocks)
+			continue;
+
+		for (j = sbi->blocks_per_seg - 1; j >= 0; j--) {
+			if (!f2fs_test_bit(j, (const char *)se->cur_valid_map))
+				continue;
+
+			from = START_BLOCK(sbi, i) + j;
+			ret = dev_read_block(raw, from);
+			ASSERT(ret >= 0);
+
+			to = from + offset;
+			ret = dev_write_block(raw, to);
+			ASSERT(ret >= 0);
+
+			get_sum_entry(sbi, from, &sum);
+
+			if (IS_DATASEG(se->type))
+				update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
+					le16_to_cpu(sum.ofs_in_node), to);
+			else
+				update_nat_blkaddr(sbi, 0,
+						le32_to_cpu(sum.nid), to);
+		}
+	}
+	free(raw);
+	DBG(0, "Info: Done to migrate Main area: main_blkaddr = 0x%x -> 0x%x\n",
+				START_BLOCK(sbi, 0),
+				START_BLOCK(sbi, 0) + offset);
+}
+
+static void move_ssa(struct f2fs_sb_info *sbi, unsigned int segno,
+					block_t new_sum_blk_addr)
+{
+	struct f2fs_summary_block *sum_blk;
+	int type;
+
+	sum_blk = get_sum_block(sbi, segno, &type);
+	if (type < SEG_TYPE_MAX) {
+		int ret;
+
+		ret = dev_write_block(sum_blk, new_sum_blk_addr);
+		ASSERT(ret >= 0);
+		DBG(1, "Write summary block: (%d) segno=%x/%x --> (%d) %x\n",
+				type, segno, GET_SUM_BLKADDR(sbi, segno),
+				IS_SUM_NODE_SEG(sum_blk->footer),
+				new_sum_blk_addr);
+	}
+	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+			type == SEG_TYPE_MAX) {
+		free(sum_blk);
+	}
+	DBG(1, "Info: Done to migrate SSA blocks\n");
+}
+
+static void migrate_ssa(struct f2fs_sb_info *sbi,
+		struct f2fs_super_block *new_sb, unsigned int offset)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	block_t old_sum_blkaddr = get_sb(ssa_blkaddr);
+	block_t new_sum_blkaddr = get_newsb(ssa_blkaddr);
+	block_t end_sum_blkaddr = get_newsb(main_blkaddr);
+	block_t expand_sum_blkaddr = new_sum_blkaddr +
+					TOTAL_SEGS(sbi) - offset;
+	block_t blkaddr;
+	int ret;
+	void *zero_block = calloc(BLOCK_SZ, 1);
+	ASSERT(zero_block);
+
+	if (offset && new_sum_blkaddr < old_sum_blkaddr + offset) {
+		blkaddr = new_sum_blkaddr;
+		while (blkaddr < end_sum_blkaddr) {
+			if (blkaddr < expand_sum_blkaddr) {
+				move_ssa(sbi, offset++, blkaddr++);
+			} else {
+				ret = dev_write_block(zero_block, blkaddr++);
+				ASSERT(ret >=0);
+			}
+		}
+	} else {
+		blkaddr = end_sum_blkaddr - 1;
+		offset = TOTAL_SEGS(sbi) - 1;
+		while (blkaddr >= new_sum_blkaddr) {
+			if (blkaddr >= expand_sum_blkaddr) {
+				ret = dev_write_block(zero_block, blkaddr--);
+				ASSERT(ret >=0);
+			} else {
+				move_ssa(sbi, offset--, blkaddr--);
+			}
+		}
+	}
+
+	DBG(0, "Info: Done to migrate SSA blocks: sum_blkaddr = 0x%x -> 0x%x\n",
+				old_sum_blkaddr, new_sum_blkaddr);
+	free(zero_block);
+}
+
+static int shrink_nats(struct f2fs_sb_info *sbi,
+				struct f2fs_super_block *new_sb)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
+	unsigned int nat_blocks;
+	void *nat_block, *zero_block;
+	int nid, ret, new_max_nid;
+	pgoff_t block_off;
+	pgoff_t block_addr;
+	int seg_off;
+
+	nat_block = malloc(BLOCK_SZ);
+	ASSERT(nat_block);
+	zero_block = calloc(BLOCK_SZ, 1);
+	ASSERT(zero_block);
+
+	nat_blocks = get_newsb(segment_count_nat) >> 1;
+	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
+	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+
+	for (nid = nm_i->max_nid - 1; nid > new_max_nid; nid -= NAT_ENTRY_PER_BLOCK) {
+		block_off = nid / NAT_ENTRY_PER_BLOCK;
+		seg_off = block_off >> sbi->log_blocks_per_seg;
+		block_addr = (pgoff_t)(old_nat_blkaddr +
+				(seg_off << sbi->log_blocks_per_seg << 1) +
+				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+
+		if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
+			block_addr += sbi->blocks_per_seg;
+
+		ret = dev_read_block(nat_block, block_addr);
+		ASSERT(ret >= 0);
+
+		if (memcmp(zero_block, nat_block, BLOCK_SZ)) {
+			ret = -1;
+			goto not_avail;
+		}
+	}
+	ret = 0;
+	nm_i->max_nid = new_max_nid;
+not_avail:
+	free(nat_block);
+	free(zero_block);
+	return ret;
+}
+
+static void migrate_nat(struct f2fs_sb_info *sbi,
+			struct f2fs_super_block *new_sb)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
+	block_t new_nat_blkaddr = get_newsb(nat_blkaddr);
+	unsigned int nat_blocks;
+	void *nat_block;
+	int nid, ret, new_max_nid;
+	pgoff_t block_off;
+	pgoff_t block_addr;
+	int seg_off;
+
+	nat_block = malloc(BLOCK_SZ);
+	ASSERT(nat_block);
+
+	for (nid = nm_i->max_nid - 1; nid >= 0; nid -= NAT_ENTRY_PER_BLOCK) {
+		block_off = nid / NAT_ENTRY_PER_BLOCK;
+		seg_off = block_off >> sbi->log_blocks_per_seg;
+		block_addr = (pgoff_t)(old_nat_blkaddr +
+				(seg_off << sbi->log_blocks_per_seg << 1) +
+				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+
+		if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
+			block_addr += sbi->blocks_per_seg;
+
+		ret = dev_read_block(nat_block, block_addr);
+		ASSERT(ret >= 0);
+
+		block_addr = (pgoff_t)(new_nat_blkaddr +
+				(seg_off << sbi->log_blocks_per_seg << 1) +
+				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+
+		/* new bitmap should be zeros */
+		ret = dev_write_block(nat_block, block_addr);
+		ASSERT(ret >= 0);
+	}
+	/* zero out newly assigned nids */
+	memset(nat_block, 0, BLOCK_SZ);
+	nat_blocks = get_newsb(segment_count_nat) >> 1;
+	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
+	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+
+	DBG(1, "Write NAT block: %x->%x, max_nid=%x->%x\n",
+			old_nat_blkaddr, new_nat_blkaddr,
+			get_sb(segment_count_nat),
+			get_newsb(segment_count_nat));
+
+	for (nid = nm_i->max_nid; nid < new_max_nid;
+				nid += NAT_ENTRY_PER_BLOCK) {
+		block_off = nid / NAT_ENTRY_PER_BLOCK;
+		seg_off = block_off >> sbi->log_blocks_per_seg;
+		block_addr = (pgoff_t)(new_nat_blkaddr +
+				(seg_off << sbi->log_blocks_per_seg << 1) +
+				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+		ret = dev_write_block(nat_block, block_addr);
+		ASSERT(ret >= 0);
+		DBG(3, "Write NAT: %lx\n", block_addr);
+	}
+	DBG(0, "Info: Done to migrate NAT blocks: nat_blkaddr = 0x%x -> 0x%x\n",
+			old_nat_blkaddr, new_nat_blkaddr);
+}
+
+static void migrate_sit(struct f2fs_sb_info *sbi,
+		struct f2fs_super_block *new_sb, unsigned int offset)
+{
+	struct sit_info *sit_i = SIT_I(sbi);
+	unsigned int ofs = 0, pre_ofs = 0;
+	unsigned int segno, index;
+	struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1);
+	block_t sit_blks = get_newsb(segment_count_sit) <<
+						(sbi->log_blocks_per_seg - 1);
+	struct seg_entry *se;
+	block_t blk_addr = 0;
+	int ret;
+
+	ASSERT(sit_blk);
+
+	/* initialize with zeros */
+	for (index = 0; index < sit_blks; index++) {
+		ret = dev_write_block(sit_blk, get_newsb(sit_blkaddr) + index);
+		ASSERT(ret >= 0);
+		DBG(3, "Write zero sit: %x\n", get_newsb(sit_blkaddr) + index);
+	}
+
+	for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
+		struct f2fs_sit_entry *sit;
+
+		se = get_seg_entry(sbi, segno);
+		if (segno < offset) {
+			ASSERT(se->valid_blocks == 0);
+			continue;
+		}
+
+		ofs = SIT_BLOCK_OFFSET(sit_i, segno - offset);
+
+		if (ofs != pre_ofs) {
+			blk_addr = get_newsb(sit_blkaddr) + pre_ofs;
+			ret = dev_write_block(sit_blk, blk_addr);
+			ASSERT(ret >= 0);
+			DBG(1, "Write valid sit: %x\n", blk_addr);
+
+			pre_ofs = ofs;
+			memset(sit_blk, 0, BLOCK_SZ);
+		}
+
+		sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno - offset)];
+		memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
+		sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+							se->valid_blocks);
+	}
+	blk_addr = get_newsb(sit_blkaddr) + ofs;
+	ret = dev_write_block(sit_blk, blk_addr);
+	DBG(1, "Write valid sit: %x\n", blk_addr);
+	ASSERT(ret >= 0);
+
+	free(sit_blk);
+	DBG(0, "Info: Done to restore new SIT blocks: 0x%x\n",
+					get_newsb(sit_blkaddr));
+}
+
+static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
+			struct f2fs_super_block *new_sb, unsigned int offset)
+{
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	struct f2fs_checkpoint *new_cp;
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	unsigned int free_segment_count, new_segment_count;
+	block_t new_cp_blks = 1 + get_newsb(cp_payload);
+	block_t orphan_blks = 0;
+	block_t new_cp_blk_no, old_cp_blk_no;
+	u_int32_t crc = 0;
+	void *buf;
+	int i, ret;
+
+	new_cp = calloc(new_cp_blks * BLOCK_SZ, 1);
+	ASSERT(new_cp);
+
+	buf = malloc(BLOCK_SZ);
+	ASSERT(buf);
+
+	/* ovp / free segments */
+	set_cp(rsvd_segment_count, c.new_reserved_segments);
+	set_cp(overprov_segment_count, (get_newsb(segment_count_main) -
+			get_cp(rsvd_segment_count)) *
+			c.new_overprovision / 100);
+	set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
+						get_cp(rsvd_segment_count));
+
+	free_segment_count = get_free_segments(sbi);
+	new_segment_count = get_newsb(segment_count_main) -
+					get_sb(segment_count_main);
+
+	set_cp(free_segment_count, free_segment_count + new_segment_count);
+	set_cp(user_block_count, ((get_newsb(segment_count_main) -
+			get_cp(overprov_segment_count)) * c.blks_per_seg));
+
+	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG))
+		orphan_blks = __start_sum_addr(sbi) - 1;
+
+	set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload));
+	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload));
+
+	/* cur->segno - offset */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		if (i < CURSEG_HOT_NODE) {
+			set_cp(cur_data_segno[i],
+					CURSEG_I(sbi, i)->segno - offset);
+		} else {
+			int n = i - CURSEG_HOT_NODE;
+
+			set_cp(cur_node_segno[n],
+					CURSEG_I(sbi, i)->segno - offset);
+		}
+	}
+
+	/* sit / nat ver bitmap bytesize */
+	set_cp(sit_ver_bitmap_bytesize,
+			((get_newsb(segment_count_sit) / 2) <<
+			get_newsb(log_blocks_per_seg)) / 8);
+	set_cp(nat_ver_bitmap_bytesize,
+			((get_newsb(segment_count_nat) / 2) <<
+			get_newsb(log_blocks_per_seg)) / 8);
+
+	memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
+						(unsigned char *)cp);
+
+	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
+	*((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+
+	/* Write a new checkpoint in the other set */
+	new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr);
+	if (sbi->cur_cp == 2)
+		old_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+	else
+		new_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+
+	/* write first cp */
+	ret = dev_write_block(new_cp, new_cp_blk_no++);
+	ASSERT(ret >= 0);
+
+	memset(buf, 0, BLOCK_SZ);
+	for (i = 0; i < get_newsb(cp_payload); i++) {
+		ret = dev_write_block(buf, new_cp_blk_no++);
+		ASSERT(ret >= 0);
+	}
+
+	for (i = 0; i < orphan_blks; i++) {
+		block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload);
+
+		ret = dev_read_block(buf, orphan_blk_no++);
+		ASSERT(ret >= 0);
+
+		ret = dev_write_block(buf, new_cp_blk_no++);
+		ASSERT(ret >= 0);
+	}
+
+	/* update summary blocks having nullified journal entries */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+
+		ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++);
+		ASSERT(ret >= 0);
+	}
+
+	/* write the last cp */
+	ret = dev_write_block(new_cp, new_cp_blk_no++);
+	ASSERT(ret >= 0);
+
+	/* disable old checkpoint */
+	memset(buf, 0, BLOCK_SZ);
+	ret = dev_write_block(buf, old_cp_blk_no);
+	ASSERT(ret >= 0);
+
+	free(buf);
+	free(new_cp);
+	DBG(0, "Info: Done to rebuild checkpoint blocks\n");
+}
+
+static void rebuild_superblock(struct f2fs_super_block *new_sb)
+{
+	int index, ret;
+	u_int8_t *buf;
+
+	buf = calloc(BLOCK_SZ, 1);
+
+	memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb));
+	for (index = 0; index < 2; index++) {
+		ret = dev_write_block(buf, index);
+		ASSERT(ret >= 0);
+	}
+	free(buf);
+	DBG(0, "Info: Done to rebuild superblock\n");
+}
+
+int f2fs_resize(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_super_block new_sb_raw;
+	struct f2fs_super_block *new_sb = &new_sb_raw;
+	block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr;
+	unsigned int offset;
+	unsigned int offset_seg = 0;
+	int err = -1;
+
+	/* flush NAT/SIT journal entries */
+	flush_journal_entries(sbi);
+
+	memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb));
+	if (get_new_sb(new_sb))
+		return -1;
+
+	/* check nat availability */
+	if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
+		err = shrink_nats(sbi, new_sb);
+		if (err) {
+			MSG(0, "\tError: Failed to shrink NATs\n");
+			return err;
+		}
+	}
+
+	print_raw_sb_info(sb);
+	print_raw_sb_info(new_sb);
+
+	old_main_blkaddr = get_sb(main_blkaddr);
+	new_main_blkaddr = get_newsb(main_blkaddr);
+	offset = new_main_blkaddr - old_main_blkaddr;
+	end_blkaddr = (get_sb(segment_count_main) <<
+			get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr);
+
+	err = -EAGAIN;
+	if (new_main_blkaddr < end_blkaddr) {
+		err = f2fs_defragment(sbi, old_main_blkaddr, offset,
+						new_main_blkaddr, 0);
+		if (!err)
+			offset_seg = offset >> get_sb(log_blocks_per_seg);
+		MSG(0, "Try to do defragement: %s\n", err ? "Skip": "Done");
+	}
+	/* move whole data region */
+	if (err)
+		migrate_main(sbi, offset);
+
+	migrate_ssa(sbi, new_sb, offset_seg);
+	migrate_nat(sbi, new_sb);
+	migrate_sit(sbi, new_sb, offset_seg);
+	rebuild_checkpoint(sbi, new_sb, offset_seg);
+	rebuild_superblock(new_sb);
+	return 0;
+}
diff --git a/fsck/segment.c b/fsck/segment.c
new file mode 100644
index 0000000..6b2f6c1
--- /dev/null
+++ b/fsck/segment.c
@@ -0,0 +1,221 @@
+/**
+ * segment.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "fsck.h"
+#include "node.h"
+
+void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
+			struct f2fs_summary *sum, int type)
+{
+	struct seg_entry *se;
+	u64 blkaddr;
+	u64 offset;
+
+	blkaddr = SM_I(sbi)->main_blkaddr;
+
+	if (find_next_free_block(sbi, &blkaddr, 0, type)) {
+		ERR_MSG("Not enough space to allocate blocks");
+		ASSERT(0);
+	}
+
+	se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr));
+	offset = OFFSET_IN_SEG(sbi, blkaddr);
+	se->type = type;
+	se->valid_blocks++;
+	f2fs_set_bit(offset, (char *)se->cur_valid_map);
+	sbi->total_valid_block_count++;
+	se->dirty = 1;
+
+	/* read/write SSA */
+	*to = (block_t)blkaddr;
+	update_sum_entry(sbi, *to, sum);
+}
+
+void new_data_block(struct f2fs_sb_info *sbi, void *block,
+				struct dnode_of_data *dn, int type)
+{
+	struct f2fs_summary sum;
+	struct node_info ni;
+
+	ASSERT(dn->node_blk);
+	memset(block, 0, BLOCK_SZ);
+
+	get_node_info(sbi, dn->nid, &ni);
+	set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
+	reserve_new_block(sbi, &dn->data_blkaddr, &sum, type);
+
+	inc_inode_blocks(dn);
+	set_data_blkaddr(dn);
+}
+
+static void f2fs_write_block(struct f2fs_sb_info *sbi, nid_t ino, void *buffer,
+					u64 count, pgoff_t offset)
+{
+	u64 start = F2FS_BYTES_TO_BLK(offset);
+	u64 len = F2FS_BYTES_TO_BLK(count);
+	u64 end_offset;
+	u64 off_in_block, len_in_block, len_already;
+	struct dnode_of_data dn = {0};
+	void *data_blk;
+	struct node_info ni;
+	struct f2fs_node *inode;
+	int ret = -1;
+
+	get_node_info(sbi, ino, &ni);
+	inode = calloc(BLOCK_SZ, 1);
+	ASSERT(inode);
+
+	ret = dev_read_block(inode, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	if (S_ISDIR(le16_to_cpu(inode->i.i_mode)) ||
+			S_ISLNK(le16_to_cpu(inode->i.i_mode)))
+		ASSERT(0);
+
+	off_in_block = offset & ((1 << F2FS_BLKSIZE_BITS) - 1);
+	len_in_block = (1 << F2FS_BLKSIZE_BITS) - off_in_block;
+	len_already = 0;
+
+	/*
+	 * When calculate how many blocks this 'count' stride accross,
+	 * We should take offset in a block in account.
+	 */
+	len = F2FS_BYTES_TO_BLK(count + off_in_block
+			+ ((1 << F2FS_BLKSIZE_BITS) - 1));
+
+	data_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(data_blk);
+
+	set_new_dnode(&dn, inode, NULL, ino);
+
+	while (len) {
+		if (dn.node_blk != dn.inode_blk)
+			free(dn.node_blk);
+
+		set_new_dnode(&dn, inode, NULL, ino);
+		get_dnode_of_data(sbi, &dn, start, ALLOC_NODE);
+
+		end_offset = ADDRS_PER_PAGE(dn.node_blk);
+
+		while (dn.ofs_in_node < end_offset && len) {
+			block_t blkaddr;
+
+			blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+
+			/* A new page from WARM_DATA */
+			if (blkaddr == NULL_ADDR)
+				new_data_block(sbi, data_blk, &dn,
+							CURSEG_WARM_DATA);
+
+			/* Copy data from buffer to file */
+			ret = dev_read_block(data_blk, dn.data_blkaddr);
+			ASSERT(ret >= 0);
+
+			memcpy(data_blk + off_in_block, buffer, len_in_block);
+
+			ret = dev_write_block(data_blk, dn.data_blkaddr);
+			ASSERT(ret >= 0);
+
+			off_in_block = 0;
+			len_already += len_in_block;
+			if ((count - len_already) > (1 << F2FS_BLKSIZE_BITS))
+				len_in_block = 1 << F2FS_BLKSIZE_BITS;
+			else
+				len_in_block = count - len_already;
+			len--;
+			start++;
+			dn.ofs_in_node++;
+		}
+		/* Update the direct node */
+		if (dn.ndirty) {
+			ret = dev_write_block(dn.node_blk, dn.node_blkaddr);
+			ASSERT(ret >= 0);
+		}
+	}
+
+	/* Update the inode info */
+	if (le64_to_cpu(inode->i.i_size) < offset + count) {
+		inode->i.i_size = cpu_to_le64(offset + count);
+		dn.idirty = 1;
+	}
+
+	if (dn.idirty) {
+		ASSERT(inode == dn.inode_blk);
+		ret = dev_write_block(inode, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
+	if (dn.node_blk && dn.node_blk != dn.inode_blk)
+		free(dn.node_blk);
+	free(data_blk);
+	free(inode);
+}
+
+int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	int fd, n;
+	pgoff_t off = 0;
+	char buffer[BLOCK_SZ];
+
+	if (de->ino == 0)
+		return -1;
+
+	fd = open(de->full_path, O_RDONLY);
+	if (fd < 0) {
+		MSG(0, "Skip: Fail to open %s\n", de->full_path);
+		return -1;
+	}
+
+	/* inline_data support */
+	if (de->size <= MAX_INLINE_DATA) {
+		struct node_info ni;
+		struct f2fs_node *node_blk;
+		int ret;
+
+		get_node_info(sbi, de->ino, &ni);
+
+		node_blk = calloc(BLOCK_SZ, 1);
+		ASSERT(node_blk);
+
+		ret = dev_read_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+
+		node_blk->i.i_inline |= F2FS_INLINE_DATA;
+		node_blk->i.i_inline |= F2FS_DATA_EXIST;
+		n = read(fd, buffer, BLOCK_SZ);
+		ASSERT(n == de->size);
+		memcpy(&node_blk->i.i_addr[1], buffer, de->size);
+
+		node_blk->i.i_size = cpu_to_le64(de->size);
+
+		ret = dev_write_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+		free(node_blk);
+	} else {
+		while ((n = read(fd, buffer, BLOCK_SZ)) > 0) {
+			f2fs_write_block(sbi, de->ino, buffer, n, off);
+			off += n;
+		}
+	}
+
+	close(fd);
+	if (n < 0)
+		return -1;
+
+	update_free_segments(sbi);
+
+	MSG(1, "Info: built a file %s, size=%lu\n", de->full_path, de->size);
+	return 0;
+}
diff --git a/fsck/sload.c b/fsck/sload.c
new file mode 100644
index 0000000..68799c1
--- /dev/null
+++ b/fsck/sload.c
@@ -0,0 +1,257 @@
+/**
+ * sload.c
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define _GNU_SOURCE
+#include "fsck.h"
+#include <libgen.h>
+#include <dirent.h>
+#include <mntent.h>
+
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
+#ifdef WITH_ANDROID
+#include <selinux/label.h>
+#include <private/android_filesystem_config.h>
+
+static void handle_selabel(struct dentry *de, int dir, char *target_out)
+{
+	uint64_t capabilities;
+	unsigned int mode = 0;
+	unsigned int uid = 0;
+	unsigned int gid = 0;
+
+	fs_config(de->path, dir, target_out, &uid,
+			&gid, &mode, &capabilities);
+	de->mode = mode;
+	de->uid = uid;
+	de->gid = gid;
+	de->capabilities = capabilities;
+}
+#else
+#define handle_selabel(...)
+#endif
+
+static int filter_dot(const struct dirent *d)
+{
+	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
+}
+
+static void f2fs_make_directory(struct f2fs_sb_info *sbi,
+				int entries, struct dentry *de)
+{
+	int i = 0;
+
+	for (i = 0; i < entries; i++) {
+		if (de[i].file_type == F2FS_FT_DIR)
+			f2fs_mkdir(sbi, de + i);
+		else if (de[i].file_type == F2FS_FT_REG_FILE)
+			f2fs_create(sbi, de + i);
+		else if (de[i].file_type == F2FS_FT_SYMLINK)
+			f2fs_symlink(sbi, de + i);
+	}
+}
+
+static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
+			const char *dir_path, const char *target_out_dir,
+			nid_t dir_ino, struct selabel_handle *sehnd)
+{
+	int entries = 0;
+	struct dentry *dentries;
+	struct dirent **namelist = NULL;
+	struct stat stat;
+	int i, ret = 0;
+
+	entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort);
+	if (entries < 0) {
+		ERR_MSG("No entries in %s\n", full_path);
+		return -ENOENT;
+	}
+
+	dentries = calloc(entries, sizeof(struct dentry));
+	if (dentries == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < entries; i++) {
+		dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name);
+		if (dentries[i].name == NULL) {
+			ERR_MSG("Skip: ENOMEM\n");
+			continue;
+		}
+		dentries[i].len = strlen((char *)dentries[i].name);
+
+		ret = asprintf(&dentries[i].path, "%s/%s",
+					dir_path, namelist[i]->d_name);
+		ASSERT(ret > 0);
+		ret = asprintf(&dentries[i].full_path, "%s/%s",
+					full_path, namelist[i]->d_name);
+		ASSERT(ret > 0);
+		free(namelist[i]);
+
+		ret = lstat(dentries[i].full_path, &stat);
+		if (ret < 0) {
+			ERR_MSG("Skip: lstat failure\n");
+			continue;
+		}
+		dentries[i].size = stat.st_size;
+		dentries[i].mode = stat.st_mode &
+			(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
+		dentries[i].mtime = stat.st_mtime;
+
+		handle_selabel(dentries + i, S_ISDIR(stat.st_mode),
+							target_out_dir);
+
+#ifdef HAVE_LIBSELINUX
+		if (sehnd && selabel_lookup(sehnd, &dentries[i].secon,
+					dentries[i].path, stat.st_mode) < 0)
+			ERR_MSG("Cannot lookup security context for %s\n",
+						dentries[i].path);
+#endif
+
+		dentries[i].pino = dir_ino;
+
+		if (S_ISREG(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_REG_FILE;
+		} else if (S_ISDIR(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_DIR;
+		} else if (S_ISCHR(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_CHRDEV;
+		} else if (S_ISBLK(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_BLKDEV;
+		} else if (S_ISFIFO(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_FIFO;
+		} else if (S_ISSOCK(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_SOCK;
+		} else if (S_ISLNK(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_SYMLINK;
+			dentries[i].link = calloc(F2FS_BLKSIZE, 1);
+			ASSERT(dentries[i].link);
+			ret = readlink(dentries[i].full_path,
+					dentries[i].link, F2FS_BLKSIZE - 1);
+			ASSERT(ret >= 0);
+		} else {
+			MSG(1, "unknown file type on %s", dentries[i].path);
+			i--;
+			entries--;
+		}
+	}
+
+	free(namelist);
+
+	f2fs_make_directory(sbi, entries, dentries);
+
+	for (i = 0; i < entries; i++) {
+		if (dentries[i].file_type == F2FS_FT_REG_FILE) {
+			f2fs_build_file(sbi, dentries + i);
+		} else if (dentries[i].file_type == F2FS_FT_DIR) {
+			char *subdir_full_path = NULL;
+			char *subdir_dir_path;
+
+			ret = asprintf(&subdir_full_path, "%s/",
+							dentries[i].full_path);
+			ASSERT(ret > 0);
+			ret = asprintf(&subdir_dir_path, "%s/",
+							dentries[i].path);
+			ASSERT(ret > 0);
+
+			build_directory(sbi, subdir_full_path, subdir_dir_path,
+					target_out_dir, dentries[i].ino, sehnd);
+			free(subdir_full_path);
+			free(subdir_dir_path);
+		} else if (dentries[i].file_type == F2FS_FT_SYMLINK) {
+			/*
+			 * It is already done in f2fs_make_directory
+			 * f2fs_make_symlink(sbi, dir_ino, &dentries[i]);
+			 */
+		} else {
+			MSG(1, "Error unknown file type\n");
+		}
+
+#ifdef HAVE_LIBSELINUX
+		if (dentries[i].secon) {
+			inode_set_selinux(sbi, dentries[i].ino, dentries[i].secon);
+			MSG(1, "File = %s \n----->SELinux context = %s\n",
+					dentries[i].path, dentries[i].secon);
+			MSG(1, "----->mode = 0x%x, uid = 0x%x, gid = 0x%x, "
+					"capabilities = 0x%lx \n",
+					dentries[i].mode, dentries[i].uid,
+					dentries[i].gid, dentries[i].capabilities);
+		}
+
+		free(dentries[i].secon);
+#endif
+
+		free(dentries[i].path);
+		free(dentries[i].full_path);
+		free((void *)dentries[i].name);
+	}
+
+	free(dentries);
+	return 0;
+}
+
+int f2fs_sload(struct f2fs_sb_info *sbi, const char *from_dir,
+				const char *mount_point,
+				const char *target_out_dir,
+				struct selabel_handle *sehnd)
+{
+	int ret = 0;
+	nid_t mnt_ino = F2FS_ROOT_INO(sbi);
+
+	/* flush NAT/SIT journal entries */
+	flush_journal_entries(sbi);
+
+	ret = f2fs_find_path(sbi, (char *)mount_point, &mnt_ino);
+	if (ret) {
+		ERR_MSG("Failed to get mount point %s\n", mount_point);
+		return ret;
+	}
+
+	ret = build_directory(sbi, from_dir, mount_point, target_out_dir,
+						mnt_ino, sehnd);
+	if (ret) {
+		ERR_MSG("Failed to build due to %d\n", ret);
+		return ret;
+	}
+
+#ifdef HAVE_LIBSELINUX
+	if (sehnd) {
+		char *secontext = NULL;
+
+		/* set root inode selinux context */
+		if (selabel_lookup(sehnd, &secontext, mount_point, S_IFDIR) < 0)
+			ERR_MSG("cannot lookup security context for %s\n",
+								mount_point);
+		if (secontext) {
+			MSG(1, "Labeling %s as %s, root_ino = %d\n",
+					mount_point, secontext, F2FS_ROOT_INO(sbi));
+			/* xattr_add for root inode */
+			inode_set_selinux(sbi, F2FS_ROOT_INO(sbi), secontext);
+		}
+		free(secontext);
+	}
+#endif
+
+	/* update curseg info; can update sit->types */
+	move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
+	zero_journal_entries(sbi);
+	write_curseg_info(sbi);
+
+	/* flush dirty sit entries */
+	flush_sit_entries(sbi);
+
+	write_checkpoint(sbi);
+	return 0;
+}
diff --git a/fsck/xattr.c b/fsck/xattr.c
new file mode 100644
index 0000000..3f5c7d3
--- /dev/null
+++ b/fsck/xattr.c
@@ -0,0 +1,241 @@
+/**
+ * xattr.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "fsck.h"
+#include "node.h"
+#include "xattr.h"
+
+#define XATTR_CREATE 0x1
+#define XATTR_REPLACE 0x2
+
+static void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
+{
+	struct f2fs_xattr_header *header;
+	void *txattr_addr;
+	u64 inline_size = inline_xattr_size(&inode->i);
+
+	txattr_addr = calloc(inline_size + BLOCK_SZ, 1);
+	ASSERT(txattr_addr);
+
+	if (inline_size)
+		memcpy(txattr_addr, inline_xattr_addr(&inode->i), inline_size);
+
+	/* Read from xattr node block. */
+	if (inode->i.i_xattr_nid) {
+		struct node_info ni;
+		int ret;
+
+		get_node_info(sbi, le32_to_cpu(inode->i.i_xattr_nid), &ni);
+		ret = dev_read_block(txattr_addr + inline_size, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
+	header = XATTR_HDR(txattr_addr);
+
+	/* Never been allocated xattrs */
+	if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) {
+		header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
+		header->h_refcount = cpu_to_le32(1);
+	}
+	return txattr_addr;
+}
+
+static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index,
+		size_t len, const char *name)
+{
+	struct f2fs_xattr_entry *entry;
+	list_for_each_xattr(entry, base_addr) {
+		if (entry->e_name_index != index)
+			continue;
+		if (entry->e_name_len != len)
+			continue;
+		if (!memcmp(entry->e_name, name, len))
+			break;
+	}
+	return entry;
+}
+
+static void write_all_xattrs(struct f2fs_sb_info *sbi,
+		struct f2fs_node *inode, __u32 hsize, void *txattr_addr)
+{
+	void *xattr_addr;
+	struct dnode_of_data dn;
+	struct node_info ni;
+	struct f2fs_node *xattr_node;
+	nid_t new_nid = 0;
+	block_t blkaddr;
+	nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid);
+	u64 inline_size = inline_xattr_size(&inode->i);
+	int ret;
+
+	ASSERT(inode->i.i_inline & F2FS_INLINE_XATTR);
+	memcpy(inline_xattr_addr(&inode->i), txattr_addr, inline_size);
+
+	if (hsize <= inline_size)
+		return;
+
+	if (!xnid) {
+		f2fs_alloc_nid(sbi, &new_nid, 0);
+
+		set_new_dnode(&dn, inode, NULL, new_nid);
+		/* NAT entry would be updated by new_node_page. */
+		blkaddr = new_node_block(sbi, &dn, XATTR_NODE_OFFSET);
+		ASSERT(dn.node_blk);
+		xattr_node = dn.node_blk;
+		inode->i.i_xattr_nid = cpu_to_le32(new_nid);
+	} else {
+		set_new_dnode(&dn, inode, NULL, xnid);
+		get_node_info(sbi, xnid, &ni);
+		blkaddr = ni.blk_addr;
+		xattr_node = calloc(BLOCK_SZ, 1);
+		ASSERT(xattr_node);
+		ret = dev_read_block(xattr_node, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
+	/* write to xattr node block */
+	xattr_addr = (void *)xattr_node;
+	memcpy(xattr_addr, txattr_addr + inline_size,
+			PAGE_SIZE - sizeof(struct node_footer));
+
+	ret = dev_write_block(xattr_node, blkaddr);
+	ASSERT(ret >= 0);
+}
+
+int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *name,
+		const void *value, size_t size, int flags)
+{
+	struct f2fs_node *inode;
+	void *base_addr;
+	struct f2fs_xattr_entry *here, *last;
+	struct node_info ni;
+	int error = 0;
+	int len;
+	int found, newsize;
+	__u32 new_hsize;
+	int ret;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	if (value == NULL)
+		return -EINVAL;
+
+	len = strlen(name);
+
+	if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN)
+		return -ERANGE;
+
+	if (ino < 3)
+		return -EINVAL;
+
+	/* Now We just support selinux */
+	ASSERT(index == F2FS_XATTR_INDEX_SECURITY);
+
+	get_node_info(sbi, ino, &ni);
+	inode = calloc(BLOCK_SZ, 1);
+	ASSERT(inode);
+	ret = dev_read_block(inode, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	base_addr = read_all_xattrs(sbi, inode);
+	ASSERT(base_addr);
+
+	here = __find_xattr(base_addr, index, len, name);
+
+	found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
+
+	if ((flags & XATTR_REPLACE) && !found) {
+		error = -ENODATA;
+		goto exit;
+	} else if ((flags & XATTR_CREATE) && found) {
+		error = -EEXIST;
+		goto exit;
+	}
+
+	last = here;
+	while (!IS_XATTR_LAST_ENTRY(last))
+		last = XATTR_NEXT_ENTRY(last);
+
+	newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size);
+
+	/* 1. Check space */
+	if (value) {
+		int free;
+		/*
+		 * If value is NULL, it is remove operation.
+		 * In case of update operation, we calculate free.
+		 */
+		free = MIN_OFFSET - ((char *)last - (char *)base_addr);
+		if (found)
+			free = free + ENTRY_SIZE(here);
+		if (free < newsize) {
+			error = -ENOSPC;
+			goto exit;
+		}
+	}
+
+	/* 2. Remove old entry */
+	if (found) {
+		/*
+		 * If entry if sound, remove old entry.
+		 * If not found, remove operation is not needed
+		 */
+		struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here);
+		int oldsize = ENTRY_SIZE(here);
+
+		memmove(here, next, (char *)last - (char *)next);
+		last = (struct f2fs_xattr_entry *)((char *)last - oldsize);
+		memset(last, 0, oldsize);
+
+	}
+
+	new_hsize = (char *)last - (char *)base_addr;
+
+	/* 3. Write new entry */
+	if (value) {
+		char *pval;
+		/*
+		 * Before we come here, old entry is removed.
+		 * We just write new entry.
+		 */
+		memset(last, 0, newsize);
+		last->e_name_index = index;
+		last->e_name_len = len;
+		memcpy(last->e_name, name, len);
+		pval = last->e_name + len;
+		memcpy(pval, value, size);
+		last->e_value_size = cpu_to_le16(size);
+		new_hsize += newsize;
+	}
+
+	write_all_xattrs(sbi, inode, new_hsize, base_addr);
+
+	/* inode need update */
+	ret = dev_write_block(inode, ni.blk_addr);
+	ASSERT(ret >= 0);
+exit:
+	free(base_addr);
+	return error;
+}
+
+int inode_set_selinux(struct f2fs_sb_info *sbi, u32 ino, const char *secon)
+{
+	if (!secon)
+		return 0;
+
+	return f2fs_setxattr(sbi, ino, F2FS_XATTR_INDEX_SECURITY,
+			XATTR_SELINUX_SUFFIX, secon, strlen(secon), 1);
+}
diff --git a/fsck/xattr.h b/fsck/xattr.h
new file mode 100644
index 0000000..b414629
--- /dev/null
+++ b/fsck/xattr.h
@@ -0,0 +1,65 @@
+/**
+ * xattr.h
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _XATTR_H_
+#define _XATTR_H_
+
+#include "f2fs.h"
+
+struct f2fs_xattr_header {
+	__le32 h_magic;		/* magic number for identification */
+	__le32 h_refcount;	/* reference count */
+	__u32 h_sloadd[4];	/* zero right now */
+};
+
+struct f2fs_xattr_entry {
+	__u8 e_name_index;
+	__u8 e_name_len;
+	__le16 e_value_size;	/* size of attribute value */
+	char e_name[0];		/* attribute name */
+};
+
+#define XATTR_ROUND	(3)
+
+#define XATTR_SELINUX_SUFFIX "selinux"
+#define F2FS_XATTR_INDEX_SECURITY	6
+#define IS_XATTR_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+
+#define XATTR_HDR(ptr)		((struct f2fs_xattr_header *)(ptr))
+#define XATTR_ENTRY(ptr) 	((struct f2fs_xattr_entry *)(ptr))
+#define F2FS_XATTR_MAGIC	0xF2F52011
+
+#define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *) ((char *)(entry) +\
+					ENTRY_SIZE(entry)))
+#define XATTR_FIRST_ENTRY(ptr)	(XATTR_ENTRY(XATTR_HDR(ptr) + 1))
+
+#define XATTR_ALIGN(size)	((size + XATTR_ROUND) & ~XATTR_ROUND)
+
+#define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \
+			entry->e_name_len + le16_to_cpu(entry->e_value_size)))
+
+#define list_for_each_xattr(entry, addr) \
+	for (entry = XATTR_FIRST_ENTRY(addr); \
+			!IS_XATTR_LAST_ENTRY(entry); \
+			entry = XATTR_NEXT_ENTRY(entry))
+
+#define MIN_OFFSET	XATTR_ALIGN(PAGE_SIZE -			\
+		sizeof(struct node_footer) - sizeof(__u32))
+
+#define MAX_VALUE_LEN	(MIN_OFFSET -				\
+		sizeof(struct f2fs_xattr_header) -		\
+		sizeof(struct f2fs_xattr_entry))
+
+#endif
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 6ce58c2..97ee297 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -20,6 +20,10 @@
 #include <config.h>
 #endif
 
+#ifdef HAVE_LINUX_BLKZONED_H
+#include <linux/blkzoned.h>
+#endif
+
 typedef u_int64_t	u64;
 typedef u_int32_t	u32;
 typedef u_int16_t	u16;
@@ -28,6 +32,7 @@
 typedef u32		nid_t;
 typedef u8		bool;
 typedef unsigned long	pgoff_t;
+typedef unsigned short	umode_t;
 
 #if HAVE_BYTESWAP_H
 #include <byteswap.h>
@@ -124,7 +129,7 @@
 	do {								\
 		printf("[ASSERT] (%s:%4d) ", __func__, __LINE__);	\
 		printf(" --> "fmt"\n", ##__VA_ARGS__);			\
-		config.bug_on = 1;					\
+		c.bug_on = 1;						\
 	} while (0)
 
 #define ASSERT(exp)							\
@@ -143,14 +148,14 @@
 
 #define MSG(n, fmt, ...)						\
 	do {								\
-		if (config.dbg_lv >= n) {				\
+		if (c.dbg_lv >= n) {					\
 			printf(fmt, ##__VA_ARGS__);			\
 		}							\
 	} while (0)
 
 #define DBG(n, fmt, ...)						\
 	do {								\
-		if (config.dbg_lv >= n) {				\
+		if (c.dbg_lv >= n) {					\
 			printf("[%s:%4d] " fmt,				\
 				__func__, __LINE__, ##__VA_ARGS__);	\
 		}							\
@@ -165,15 +170,17 @@
 #define DISP_u32(ptr, member)						\
 	do {								\
 		assert(sizeof((ptr)->member) <= 4);			\
-		printf("%-30s" "\t\t[0x%8x : %u]\n",		\
-			#member, ((ptr)->member), ((ptr)->member));	\
+		printf("%-30s" "\t\t[0x%8x : %u]\n",			\
+			#member, le32_to_cpu(((ptr)->member)),		\
+			le32_to_cpu(((ptr)->member)));			\
 	} while (0)
 
 #define DISP_u64(ptr, member)						\
 	do {								\
 		assert(sizeof((ptr)->member) == 8);			\
 		printf("%-30s" "\t\t[0x%8llx : %llu]\n",		\
-			#member, ((ptr)->member), ((ptr)->member));	\
+			#member, le64_to_cpu(((ptr)->member)),		\
+			le64_to_cpu(((ptr)->member)));			\
 	} while (0)
 
 #define DISP_utf(ptr, member)						\
@@ -207,9 +214,13 @@
 #define BITS_PER_BYTE		8
 #define F2FS_SUPER_MAGIC	0xF2F52010	/* F2FS Magic Number */
 #define CHECKSUM_OFFSET		4092
+#define MAX_PATH_LEN		64
+#define MAX_DEVICES		8
+
+#define F2FS_BYTES_TO_BLK(bytes)    ((bytes) >> F2FS_BLKSIZE_BITS)
+#define F2FS_BLKSIZE_BITS 12
 
 /* for mkfs */
-#define F2FS_MIN_VOLUME_SIZE	104857600
 #define	F2FS_NUMBER_OF_CHECKPOINT_PACK	2
 #define	DEFAULT_SECTOR_SIZE		512
 #define	DEFAULT_SECTORS_PER_BLOCK	8
@@ -221,18 +232,45 @@
 enum f2fs_config_func {
 	FSCK,
 	DUMP,
+	DEFRAG,
+	RESIZE,
+	SLOAD,
+};
+
+struct device_info {
+	char *path;
+	int32_t fd;
+	u_int32_t sector_size;
+	u_int64_t total_sectors;	/* got by get_device_info */
+	u_int64_t start_blkaddr;
+	u_int64_t end_blkaddr;
+	u_int32_t total_segments;
+
+	/* to handle zone block devices */
+	int zoned_model;
+	u_int32_t nr_zones;
+	u_int32_t nr_rnd_zones;
+	size_t zone_blocks;
 };
 
 struct f2fs_configuration {
-	u_int32_t sector_size;
 	u_int32_t reserved_segments;
-	u_int32_t overprovision;
+	u_int32_t new_reserved_segments;
+	int zoned_mode;
+	int zoned_model;
+	size_t zone_blocks;
+	double overprovision;
+	double new_overprovision;
 	u_int32_t cur_seg[6];
 	u_int32_t segs_per_sec;
 	u_int32_t secs_per_zone;
 	u_int32_t segs_per_zone;
 	u_int32_t start_sector;
+	u_int32_t total_segments;
+	u_int32_t sector_size;
 	u_int64_t total_sectors;
+	u_int64_t wanted_total_sectors;
+	u_int64_t target_sectors;
 	u_int32_t sectors_per_blk;
 	u_int32_t blks_per_seg;
 	__u8 init_version[VERSION_LEN + 1];
@@ -240,10 +278,12 @@
 	__u8 version[VERSION_LEN + 1];
 	char *vol_label;
 	int heap;
-	int32_t fd, kd;
+	int32_t kd;
 	int32_t dump_fd;
-	char *device_name;
+	struct device_info devices[MAX_DEVICES];
+	int ndevs;
 	char *extension_list;
+	const char *rootdev_name;
 	int dbg_lv;
 	int trim;
 	int func;
@@ -251,6 +291,19 @@
 	int fix_on;
 	int bug_on;
 	int auto_fix;
+	int preen_mode;
+	int ro;
+	__le32 feature;			/* defined features */
+
+	/* defragmentation parameters */
+	int defrag_shrink;
+	u_int64_t defrag_start;
+	u_int64_t defrag_len;
+	u_int64_t defrag_target;
+
+	/* sload parameters */
+	char *from_dir;
+	char *mount_point;
 } __attribute__((packed));
 
 #ifdef CONFIG_64BIT
@@ -262,6 +315,92 @@
 #define BIT_MASK(nr)	(1 << (nr % BITS_PER_LONG))
 #define BIT_WORD(nr)	(nr / BITS_PER_LONG)
 
+#define set_sb_le64(member, val)		(sb->member = cpu_to_le64(val))
+#define set_sb_le32(member, val)		(sb->member = cpu_to_le32(val))
+#define set_sb_le16(member, val)		(sb->member = cpu_to_le16(val))
+#define get_sb_le64(member)			le64_to_cpu(sb->member)
+#define get_sb_le32(member)			le32_to_cpu(sb->member)
+#define get_sb_le16(member)			le16_to_cpu(sb->member)
+#define get_newsb_le64(member)			le64_to_cpu(new_sb->member)
+#define get_newsb_le32(member)			le32_to_cpu(new_sb->member)
+#define get_newsb_le16(member)			le16_to_cpu(new_sb->member)
+
+#define set_sb(member, val)	\
+			do {						\
+				typeof(sb->member) t;			\
+				switch (sizeof(t)) {			\
+				case 8: set_sb_le64(member, val); break; \
+				case 4: set_sb_le32(member, val); break; \
+				case 2: set_sb_le16(member, val); break; \
+				} \
+			} while(0)
+
+#define get_sb(member)		\
+			({						\
+				typeof(sb->member) t;			\
+				switch (sizeof(t)) {			\
+				case 8: t = get_sb_le64(member); break; \
+				case 4: t = get_sb_le32(member); break; \
+				case 2: t = get_sb_le16(member); break; \
+				} 					\
+				t; \
+			})
+#define get_newsb(member)		\
+			({						\
+				typeof(new_sb->member) t;		\
+				switch (sizeof(t)) {			\
+				case 8: t = get_newsb_le64(member); break; \
+				case 4: t = get_newsb_le32(member); break; \
+				case 2: t = get_newsb_le16(member); break; \
+				} 					\
+				t; \
+			})
+
+#define set_cp_le64(member, val)		(cp->member = cpu_to_le64(val))
+#define set_cp_le32(member, val)		(cp->member = cpu_to_le32(val))
+#define set_cp_le16(member, val)		(cp->member = cpu_to_le16(val))
+#define get_cp_le64(member)			le64_to_cpu(cp->member)
+#define get_cp_le32(member)			le32_to_cpu(cp->member)
+#define get_cp_le16(member)			le16_to_cpu(cp->member)
+
+#define set_cp(member, val)	\
+			do {						\
+				typeof(cp->member) t;			\
+				switch (sizeof(t)) {			\
+				case 8: set_cp_le64(member, val); break; \
+				case 4: set_cp_le32(member, val); break; \
+				case 2: set_cp_le16(member, val); break; \
+				} \
+			} while(0)
+
+#define get_cp(member)		\
+			({						\
+				typeof(cp->member) t;			\
+				switch (sizeof(t)) {			\
+				case 8: t = get_cp_le64(member); break; \
+				case 4: t = get_cp_le32(member); break; \
+				case 2: t = get_cp_le16(member); break; \
+				} 					\
+				t; \
+			})
+
+/*
+ * Copied from include/linux/kernel.h
+ */
+#define __round_mask(x, y)	((__typeof__(x))((y)-1))
+#define round_down(x, y)	((x) & ~__round_mask(x, y))
+#define min(x, y) ({				\
+	typeof(x) _min1 = (x);			\
+	typeof(y) _min2 = (y);			\
+	(void) (&_min1 == &_min2);		\
+	_min1 < _min2 ? _min1 : _min2; })
+
+#define max(x, y) ({				\
+	typeof(x) _max1 = (x);			\
+	typeof(y) _max2 = (y);			\
+	(void) (&_max1 == &_max2);		\
+	_max1 > _max2 ? _max1 : _max2; })
+
 /*
  * Copied from fs/f2fs/f2fs.h
  */
@@ -279,6 +418,8 @@
 	NO_CHECK_TYPE
 };
 
+#define F2FS_MIN_SEGMENTS	9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */
+
 /*
  * Copied from fs/f2fs/segment.h
  */
@@ -315,9 +456,19 @@
 #define MAX_ACTIVE_NODE_LOGS	8
 #define MAX_ACTIVE_DATA_LOGS	8
 
+#define F2FS_FEATURE_ENCRYPT	0x0001
+#define F2FS_FEATURE_BLKZONED	0x0002
+
+#define MAX_VOLUME_NAME		512
+
 /*
  * For superblock
  */
+struct f2fs_device {
+	__u8 path[MAX_PATH_LEN];
+	__le32 total_segments;
+} __attribute__((packed));
+
 struct f2fs_super_block {
 	__le32 magic;			/* Magic Number */
 	__le16 major_ver;		/* Major Version */
@@ -347,12 +498,17 @@
 	__le32 node_ino;		/* node inode number */
 	__le32 meta_ino;		/* meta inode number */
 	__u8 uuid[16];			/* 128-bit uuid for volume */
-	__le16 volume_name[512];	/* volume name */
+	__le16 volume_name[MAX_VOLUME_NAME];	/* volume name */
 	__le32 extension_count;		/* # of extensions below */
 	__u8 extension_list[F2FS_MAX_EXTENSION][8];	/* extension array */
 	__le32 cp_payload;
 	__u8 version[VERSION_LEN];	/* the kernel version */
 	__u8 init_version[VERSION_LEN];	/* the initial kernel version */
+	__le32 feature;			/* defined features */
+	__u8 encryption_level;		/* versioning level for encryption */
+	__u8 encrypt_pw_salt[16];	/* Salt used for string2key algorithm */
+	struct f2fs_device devs[MAX_DEVICES];	/* device list */
+	__u8 reserved[327];		/* valid reserved region */
 } __attribute__((packed));
 
 /*
@@ -422,7 +578,9 @@
 #define F2FS_NAME_LEN		255
 #define F2FS_INLINE_XATTR_ADDRS	50	/* 200 bytes for inline xattrs */
 #define DEF_ADDRS_PER_INODE	923	/* Address Pointers in an Inode */
-#define ADDRS_PER_INODE(fi)	addrs_per_inode(fi)
+#define ADDRS_PER_INODE(i)	addrs_per_inode(i)
+#define DEF_ADDRS_PER_INODE_INLINE_XATTR				\
+		(DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS)
 #define ADDRS_PER_BLOCK         1018	/* Address Pointers in a Direct Block */
 #define NIDS_PER_BLOCK          1018	/* Node IDs in an Indirect Block */
 
@@ -436,15 +594,25 @@
 #define F2FS_INLINE_DATA	0x02	/* file inline data flag */
 #define F2FS_INLINE_DENTRY	0x04	/* file inline dentry flag */
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
+#define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 
-#define MAX_INLINE_DATA		(sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
-						F2FS_INLINE_XATTR_ADDRS - 1))
+#define MAX_INLINE_DATA (sizeof(__le32) *				\
+			(DEF_ADDRS_PER_INODE_INLINE_XATTR - 1))
 
 #define INLINE_DATA_OFFSET	(PAGE_CACHE_SIZE - sizeof(struct node_footer) \
 				- sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - 1))
 
 #define DEF_DIR_LEVEL		0
 
+/*
+ * i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
+ */
+#define FADVISE_COLD_BIT       0x01
+#define FADVISE_LOST_PINO_BIT  0x02
+#define FADVISE_ENCRYPT_BIT    0x04
+
+#define file_is_encrypt(fi)      ((fi)->i_advise & FADVISE_ENCRYPT_BIT)
+
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
 	__u8 i_advise;			/* file hints */
@@ -517,6 +685,7 @@
  * For NAT entries
  */
 #define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry))
+#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
 
 struct f2fs_nat_entry {
 	__u8 version;		/* latest version of cached nat entry */
@@ -543,7 +712,9 @@
  * disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments.
  */
 #define F2FS_MAX_SEGMENT       ((16 * 1024 * 1024) / 2)
-#define MAX_SIT_BITMAP_SIZE    ((F2FS_MAX_SEGMENT / SIT_ENTRY_PER_BLOCK) / 8)
+#define MAX_SIT_BITMAP_SIZE    (SEG_ALIGN(ALIGN(F2FS_MAX_SEGMENT, \
+						SIT_ENTRY_PER_BLOCK)) * \
+						c.blks_per_seg / 8)
 
 /*
  * Note that f2fs_sit_entry->vblocks has the following bit-field information.
@@ -606,7 +777,7 @@
 
 struct summary_footer {
 	unsigned char entry_type;	/* SUM_TYPE_XXX */
-	__u32 check_sum;		/* summary checksum */
+	__le32 check_sum;		/* summary checksum */
 } __attribute__((packed));
 
 #define SUM_JOURNAL_SIZE	(F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
@@ -619,6 +790,13 @@
 				sizeof(struct sit_journal_entry))
 #define SIT_JOURNAL_RESERVED	((SUM_JOURNAL_SIZE - 2) %\
 				sizeof(struct sit_journal_entry))
+
+/*
+ * Reserved area should make size of f2fs_extra_info equals to
+ * that of nat_journal and sit_journal.
+ */
+#define EXTRA_INFO_RESERVED	(SUM_JOURNAL_SIZE - 2 - 8)
+
 /*
  * frequently updated NAT/SIT entries can be stored in the spare area in
  * summary blocks
@@ -648,18 +826,28 @@
 	__u8 reserved[SIT_JOURNAL_RESERVED];
 } __attribute__((packed));
 
-/* 4KB-sized summary block structure */
-struct f2fs_summary_block {
-	struct f2fs_summary entries[ENTRIES_IN_SUM];
+struct f2fs_extra_info {
+	__le64 kbytes_written;
+	__u8 reserved[EXTRA_INFO_RESERVED];
+} __attribute__((packed));
+
+struct f2fs_journal {
 	union {
 		__le16 n_nats;
 		__le16 n_sits;
 	};
-	/* spare area is used by NAT or SIT journals */
+	/* spare area is used by NAT or SIT journals or extra info */
 	union {
 		struct nat_journal nat_j;
 		struct sit_journal sit_j;
+		struct f2fs_extra_info info;
 	};
+} __attribute__((packed));
+
+/* 4KB-sized summary block structure */
+struct f2fs_summary_block {
+	struct f2fs_summary entries[ENTRIES_IN_SUM];
+	struct f2fs_journal journal;
 	struct summary_footer footer;
 } __attribute__((packed));
 
@@ -685,6 +873,9 @@
 /* MAX level for dir lookup */
 #define MAX_DIR_HASH_DEPTH	63
 
+/* MAX buckets in one level of dir */
+#define MAX_DIR_BUCKETS		(1 << ((MAX_DIR_HASH_DEPTH / 2) - 1))
+
 #define SIZE_OF_DIR_ENTRY	11	/* by byte */
 #define SIZE_OF_DENTRY_BITMAP	((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
 					BITS_PER_BYTE)
@@ -725,7 +916,7 @@
 	__u8 reserved[INLINE_RESERVED_SIZE];
 	struct f2fs_dir_entry dentry[NR_INLINE_DENTRY];
 	__u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN];
-} __packed;
+} __attribute__((packed));
 
 /* file types used in inode_info->flags */
 enum FILE_TYPE {
@@ -750,27 +941,30 @@
 	SSR
 };
 
-extern void ASCIIToUNICODE(u_int16_t *, u_int8_t *);
+extern int utf8_to_utf16(u_int16_t *, const char *, size_t, size_t);
+extern int utf16_to_utf8(char *, const u_int16_t *, size_t, size_t);
 extern int log_base_2(u_int32_t);
 extern unsigned int addrs_per_inode(struct f2fs_inode *);
 
 extern int get_bits_in_byte(unsigned char n);
-extern int set_bit(unsigned int nr,void * addr);
-extern int clear_bit(unsigned int nr, void * addr);
-extern int test_bit(unsigned int nr, const void * addr);
+extern int test_and_set_bit_le(u32, u8 *);
+extern int test_and_clear_bit_le(u32, u8 *);
+extern int test_bit_le(u32, const u8 *);
 extern int f2fs_test_bit(unsigned int, const char *);
 extern int f2fs_set_bit(unsigned int, char *);
 extern int f2fs_clear_bit(unsigned int, char *);
-extern unsigned long find_next_bit(const unsigned long *,
-				unsigned long, unsigned long);
+extern u64 find_next_bit_le(const u8 *, u64, u64);
+extern u64 find_next_zero_bit_le(const u8 *, u64, u64);
 
 extern u_int32_t f2fs_cal_crc32(u_int32_t, void *, int);
 extern int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len);
 
-extern void f2fs_init_configuration(struct f2fs_configuration *);
-extern int f2fs_dev_is_umounted(struct f2fs_configuration *);
-extern int f2fs_get_device_info(struct f2fs_configuration *);
-extern void f2fs_finalize_device(struct f2fs_configuration *);
+extern void f2fs_init_configuration(void);
+extern int f2fs_devs_are_umounted(void);
+extern int f2fs_dev_is_umounted(char *);
+extern int f2fs_get_device_info(void);
+extern int get_device_info(int);
+extern void f2fs_finalize_device(void);
 
 extern int dev_read(void *, __u64, size_t);
 extern int dev_write(void *, __u64, size_t);
@@ -778,20 +972,115 @@
 extern int dev_write_dump(void *, __u64, size_t);
 /* All bytes in the buffer must be 0 use dev_fill(). */
 extern int dev_fill(void *, __u64, size_t);
+extern int dev_fill_block(void *, __u64);
 
 extern int dev_read_block(void *, __u64);
-extern int dev_read_blocks(void *, __u64, __u32 );
 extern int dev_reada_block(__u64);
 
 extern int dev_read_version(void *, __u64, size_t);
 extern void get_kernel_version(__u8 *);
 f2fs_hash_t f2fs_dentry_hash(const unsigned char *, int);
 
-extern struct f2fs_configuration config;
+#define F2FS_ZONED_NONE		0
+#define F2FS_ZONED_HA		1
+#define F2FS_ZONED_HM		2
+
+#ifdef HAVE_LINUX_BLKZONED_H
+
+#define blk_zone_type(z)        (z)->type
+#define blk_zone_conv(z)	((z)->type == BLK_ZONE_TYPE_CONVENTIONAL)
+#define blk_zone_seq_req(z)	((z)->type == BLK_ZONE_TYPE_SEQWRITE_REQ)
+#define blk_zone_seq_pref(z)	((z)->type == BLK_ZONE_TYPE_SEQWRITE_PREF)
+#define blk_zone_seq(z)		(blk_zone_seq_req(z) || blk_zone_seq_pref(z))
+
+static inline const char *
+blk_zone_type_str(struct blk_zone *blkz)
+{
+	switch (blk_zone_type(blkz)) {
+	case BLK_ZONE_TYPE_CONVENTIONAL:
+		return( "Conventional" );
+	case BLK_ZONE_TYPE_SEQWRITE_REQ:
+		return( "Sequential-write-required" );
+	case BLK_ZONE_TYPE_SEQWRITE_PREF:
+		return( "Sequential-write-preferred" );
+	}
+	return( "Unknown-type" );
+}
+
+#define blk_zone_cond(z)	(z)->cond
+
+static inline const char *
+blk_zone_cond_str(struct blk_zone *blkz)
+{
+	switch (blk_zone_cond(blkz)) {
+	case BLK_ZONE_COND_NOT_WP:
+		return "Not-write-pointer";
+	case BLK_ZONE_COND_EMPTY:
+		return "Empty";
+	case BLK_ZONE_COND_IMP_OPEN:
+		return "Implicit-open";
+	case BLK_ZONE_COND_EXP_OPEN:
+		return "Explicit-open";
+	case BLK_ZONE_COND_CLOSED:
+		return "Closed";
+	case BLK_ZONE_COND_READONLY:
+		return "Read-only";
+	case BLK_ZONE_COND_FULL:
+		return "Full";
+	case BLK_ZONE_COND_OFFLINE:
+		return "Offline";
+	}
+	return "Unknown-cond";
+}
+
+#define blk_zone_empty(z)	(blk_zone_cond(z) == BLK_ZONE_COND_EMPTY)
+
+#define blk_zone_sector(z)	(z)->start
+#define blk_zone_length(z)	(z)->len
+#define blk_zone_wp_sector(z)	(z)->wp
+#define blk_zone_need_reset(z)	(int)(z)->reset
+#define blk_zone_non_seq(z)	(int)(z)->non_seq
+
+#endif
+
+extern void f2fs_get_zoned_model(int);
+extern int f2fs_get_zone_blocks(int);
+extern int f2fs_check_zones(int);
+extern int f2fs_reset_zones(int);
+
+extern struct f2fs_configuration c;
 
 #define ALIGN(val, size)	((val) + (size) - 1) / (size)
-#define SEG_ALIGN(blks)		ALIGN(blks, config.blks_per_seg)
-#define ZONE_ALIGN(blks)	ALIGN(blks, config.blks_per_seg * \
-					config.segs_per_zone)
+#define SEG_ALIGN(blks)		ALIGN(blks, c.blks_per_seg)
+#define ZONE_ALIGN(blks)	ALIGN(blks, c.blks_per_seg * \
+					c.segs_per_zone)
+
+static inline double get_best_overprovision(struct f2fs_super_block *sb)
+{
+	double reserved, ovp, candidate, end, diff, space;
+	double max_ovp = 0, max_space = 0;
+
+	if (get_sb(segment_count_main) < 256) {
+		candidate = 10;
+		end = 95;
+		diff = 5;
+	} else {
+		candidate = 0.01;
+		end = 10;
+		diff = 0.01;
+	}
+
+	for (; candidate <= end; candidate += diff) {
+		reserved = (2 * (100 / candidate + 1) + 6) *
+						get_sb(segs_per_sec);
+		ovp = (get_sb(segment_count_main) - reserved) * candidate / 100;
+		space = get_sb(segment_count_main) - reserved - ovp;
+		if (max_space < space) {
+			max_space = space;
+			max_ovp = candidate;
+		}
+	}
+	return max_ovp;
+}
 
 #endif	/*__F2FS_FS_H */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a6b304c..b26b404 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -2,6 +2,7 @@
 
 lib_LTLIBRARIES = libf2fs.la
 
-libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c
+libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c libf2fs_zoned.c
 libf2fs_la_CFLAGS = -Wall
 libf2fs_la_CPPFLAGS = -I$(top_srcdir)/include
+libf2fs_la_LDFLAGS = -version-info $(LIBF2FS_CURRENT):$(LIBF2FS_REVISION):$(LIBF2FS_AGE)
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 9e19e9b..93d3da9 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -19,22 +19,190 @@
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <sys/ioctl.h>
+#ifndef WITH_ANDROID
+#include <scsi/sg.h>
+#endif
 #include <linux/hdreg.h>
+#include <linux/limits.h>
 
 #include <f2fs_fs.h>
 
-void ASCIIToUNICODE(u_int16_t *out_buf, u_int8_t *in_buf)
-{
-	u_int8_t *pchTempPtr = in_buf;
-	u_int16_t *pwTempPtr = out_buf;
+#ifndef WITH_ANDROID
+/* SCSI command for standard inquiry*/
+#define MODELINQUIRY	0x12,0x00,0x00,0x00,0x4A,0x00
+#endif
 
-	while (*pchTempPtr != '\0') {
-		*pwTempPtr = (u_int16_t)*pchTempPtr;
-		pchTempPtr++;
-		pwTempPtr++;
+/*
+ * UTF conversion codes are Copied from exfat tools.
+ */
+static const char *utf8_to_wchar(const char *input, wchar_t *wc,
+		size_t insize)
+{
+	if ((input[0] & 0x80) == 0 && insize >= 1) {
+		*wc = (wchar_t) input[0];
+		return input + 1;
 	}
-	*pwTempPtr = '\0';
-	return;
+	if ((input[0] & 0xe0) == 0xc0 && insize >= 2) {
+		*wc = (((wchar_t) input[0] & 0x1f) << 6) |
+		       ((wchar_t) input[1] & 0x3f);
+		return input + 2;
+	}
+	if ((input[0] & 0xf0) == 0xe0 && insize >= 3) {
+		*wc = (((wchar_t) input[0] & 0x0f) << 12) |
+		      (((wchar_t) input[1] & 0x3f) << 6) |
+		       ((wchar_t) input[2] & 0x3f);
+		return input + 3;
+	}
+	if ((input[0] & 0xf8) == 0xf0 && insize >= 4) {
+		*wc = (((wchar_t) input[0] & 0x07) << 18) |
+		      (((wchar_t) input[1] & 0x3f) << 12) |
+		      (((wchar_t) input[2] & 0x3f) << 6) |
+		       ((wchar_t) input[3] & 0x3f);
+		return input + 4;
+	}
+	if ((input[0] & 0xfc) == 0xf8 && insize >= 5) {
+		*wc = (((wchar_t) input[0] & 0x03) << 24) |
+		      (((wchar_t) input[1] & 0x3f) << 18) |
+		      (((wchar_t) input[2] & 0x3f) << 12) |
+		      (((wchar_t) input[3] & 0x3f) << 6) |
+		       ((wchar_t) input[4] & 0x3f);
+		return input + 5;
+	}
+	if ((input[0] & 0xfe) == 0xfc && insize >= 6) {
+		*wc = (((wchar_t) input[0] & 0x01) << 30) |
+		      (((wchar_t) input[1] & 0x3f) << 24) |
+		      (((wchar_t) input[2] & 0x3f) << 18) |
+		      (((wchar_t) input[3] & 0x3f) << 12) |
+		      (((wchar_t) input[4] & 0x3f) << 6) |
+		       ((wchar_t) input[5] & 0x3f);
+		return input + 6;
+	}
+	return NULL;
+}
+
+static u_int16_t *wchar_to_utf16(u_int16_t *output, wchar_t wc, size_t outsize)
+{
+	if (wc <= 0xffff) {
+		if (outsize == 0)
+			return NULL;
+		output[0] = cpu_to_le16(wc);
+		return output + 1;
+	}
+	if (outsize < 2)
+		return NULL;
+	wc -= 0x10000;
+	output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff));
+	output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff));
+	return output + 2;
+}
+
+int utf8_to_utf16(u_int16_t *output, const char *input, size_t outsize,
+		size_t insize)
+{
+	const char *inp = input;
+	u_int16_t *outp = output;
+	wchar_t wc;
+
+	while ((size_t)(inp - input) < insize && *inp) {
+		inp = utf8_to_wchar(inp, &wc, insize - (inp - input));
+		if (inp == NULL) {
+			DBG(0, "illegal UTF-8 sequence\n");
+			return -EILSEQ;
+		}
+		outp = wchar_to_utf16(outp, wc, outsize - (outp - output));
+		if (outp == NULL) {
+			DBG(0, "name is too long\n");
+			return -ENAMETOOLONG;
+		}
+	}
+	*outp = cpu_to_le16(0);
+	return 0;
+}
+
+static const u_int16_t *utf16_to_wchar(const u_int16_t *input, wchar_t *wc,
+		size_t insize)
+{
+	if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800) {
+		if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00)
+			return NULL;
+		*wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10);
+		*wc |= (le16_to_cpu(input[1]) & 0x3ff);
+		*wc += 0x10000;
+		return input + 2;
+	} else {
+		*wc = le16_to_cpu(*input);
+		return input + 1;
+	}
+}
+
+static char *wchar_to_utf8(char *output, wchar_t wc, size_t outsize)
+{
+	if (wc <= 0x7f) {
+		if (outsize < 1)
+			return NULL;
+		*output++ = (char) wc;
+	} else if (wc <= 0x7ff) {
+		if (outsize < 2)
+			return NULL;
+		*output++ = 0xc0 | (wc >> 6);
+		*output++ = 0x80 | (wc & 0x3f);
+	} else if (wc <= 0xffff) {
+		if (outsize < 3)
+			return NULL;
+		*output++ = 0xe0 | (wc >> 12);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	} else if (wc <= 0x1fffff) {
+		if (outsize < 4)
+			return NULL;
+		*output++ = 0xf0 | (wc >> 18);
+		*output++ = 0x80 | ((wc >> 12) & 0x3f);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	} else if (wc <= 0x3ffffff) {
+		if (outsize < 5)
+			return NULL;
+		*output++ = 0xf8 | (wc >> 24);
+		*output++ = 0x80 | ((wc >> 18) & 0x3f);
+		*output++ = 0x80 | ((wc >> 12) & 0x3f);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	} else if (wc <= 0x7fffffff) {
+		if (outsize < 6)
+			return NULL;
+		*output++ = 0xfc | (wc >> 30);
+		*output++ = 0x80 | ((wc >> 24) & 0x3f);
+		*output++ = 0x80 | ((wc >> 18) & 0x3f);
+		*output++ = 0x80 | ((wc >> 12) & 0x3f);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	} else
+		return NULL;
+
+	return output;
+}
+
+int utf16_to_utf8(char *output, const u_int16_t *input, size_t outsize,
+		size_t insize)
+{
+	const u_int16_t *inp = input;
+	char *outp = output;
+	wchar_t wc;
+
+	while ((size_t)(inp - input) < insize && le16_to_cpu(*inp)) {
+		inp = utf16_to_wchar(inp, &wc, insize - (inp - input));
+		if (inp == NULL) {
+			DBG(0, "illegal UTF-16 sequence\n");
+			return -EILSEQ;
+		}
+		outp = wchar_to_utf8(outp, wc, outsize - (outp - output));
+		if (outp == NULL) {
+			DBG(0, "name is too long\n");
+			return -ENAMETOOLONG;
+		}
+	}
+	*outp = '\0';
+	return 0;
 }
 
 int log_base_2(u_int32_t num)
@@ -75,37 +243,31 @@
 	return bits_in_byte[n];
 }
 
-int set_bit(unsigned int nr,void * addr)
+int test_and_set_bit_le(u32 nr, u8 *addr)
 {
-	int             mask, retval;
-	unsigned char   *ADDR = (unsigned char *) addr;
+	int mask, retval;
 
-	ADDR += nr >> 3;
+	addr += nr >> 3;
 	mask = 1 << ((nr & 0x07));
-	retval = mask & *ADDR;
-	*ADDR |= mask;
+	retval = mask & *addr;
+	*addr |= mask;
 	return retval;
 }
 
-int clear_bit(unsigned int nr, void * addr)
+int test_and_clear_bit_le(u32 nr, u8 *addr)
 {
-	int             mask, retval;
-	unsigned char   *ADDR = (unsigned char *) addr;
+	int mask, retval;
 
-	ADDR += nr >> 3;
+	addr += nr >> 3;
 	mask = 1 << ((nr & 0x07));
-	retval = mask & *ADDR;
-	*ADDR &= ~mask;
+	retval = mask & *addr;
+	*addr &= ~mask;
 	return retval;
 }
 
-int test_bit(unsigned int nr, const void * addr)
+int test_bit_le(u32 nr, const u8 *addr)
 {
-	const __u32 *p = (const __u32 *)addr;
-
-	nr = nr ^ 0;
-
-	return ((1 << (nr & 31)) & (p[nr >> 5])) != 0;
+	return ((1 << (nr & 7)) & (addr[nr >> 3]));
 }
 
 int f2fs_test_bit(unsigned int nr, const char *p)
@@ -142,24 +304,10 @@
 	return ret;
 }
 
-static inline unsigned long __ffs(unsigned long word)
+static inline u64 __ffs(u8 word)
 {
 	int num = 0;
 
-#if BITS_PER_LONG == 64
-	if ((word & 0xffffffff) == 0) {
-		num += 32;
-		word >>= 32;
-	}
-#endif
-	if ((word & 0xffff) == 0) {
-		num += 16;
-		word >>= 16;
-	}
-	if ((word & 0xff) == 0) {
-		num += 8;
-		word >>= 8;
-	}
 	if ((word & 0xf) == 0) {
 		num += 4;
 		word >>= 4;
@@ -173,43 +321,42 @@
 	return num;
 }
 
-unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
-                unsigned long offset)
+/* Copied from linux/lib/find_bit.c */
+#define BITMAP_FIRST_BYTE_MASK(start) (0xff << ((start) & (BITS_PER_BYTE - 1)))
+
+static u64 _find_next_bit_le(const u8 *addr, u64 nbits, u64 start, char invert)
 {
-        const unsigned long *p = addr + BIT_WORD(offset);
-        unsigned long result = offset & ~(BITS_PER_LONG-1);
-        unsigned long tmp;
+	u8 tmp;
 
-        if (offset >= size)
-                return size;
-        size -= result;
-        offset %= BITS_PER_LONG;
-        if (offset) {
-                tmp = *(p++);
-                tmp &= (~0UL << offset);
-                if (size < BITS_PER_LONG)
-                        goto found_first;
-                if (tmp)
-                        goto found_middle;
-                size -= BITS_PER_LONG;
-                result += BITS_PER_LONG;
-        }
-        while (size & ~(BITS_PER_LONG-1)) {
-                if ((tmp = *(p++)))
-                        goto found_middle;
-                result += BITS_PER_LONG;
-                size -= BITS_PER_LONG;
-        }
-        if (!size)
-                return result;
-        tmp = *p;
+	if (!nbits || start >= nbits)
+		return nbits;
 
-found_first:
-        tmp &= (~0UL >> (BITS_PER_LONG - size));
-        if (tmp == 0UL)		/* Are any bits set? */
-                return result + size;   /* Nope. */
-found_middle:
-        return result + __ffs(tmp);
+	tmp = addr[start / BITS_PER_BYTE] ^ invert;
+
+	/* Handle 1st word. */
+	tmp &= BITMAP_FIRST_BYTE_MASK(start);
+	start = round_down(start, BITS_PER_BYTE);
+
+	while (!tmp) {
+		start += BITS_PER_BYTE;
+		if (start >= nbits)
+			return nbits;
+
+		tmp = addr[start / BITS_PER_BYTE] ^ invert;
+	}
+
+	return min(start + __ffs(tmp), nbits);
+}
+
+u64 find_next_bit_le(const u8 *addr, u64 size, u64 offset)
+{
+	return _find_next_bit_le(addr, size, offset, 0);
+}
+
+
+u64 find_next_zero_bit_le(const u8 *addr, u64 size, u64 offset)
+{
+	return _find_next_bit_le(addr, size, offset, 0xff);
 }
 
 /*
@@ -343,25 +490,89 @@
 }
 
 /*
+ * try to identify the root device
+ */
+const char *get_rootdev()
+{
+	struct stat sb;
+	int fd, ret;
+	char buf[32];
+	char *uevent, *ptr;
+
+	static char rootdev[PATH_MAX + 1];
+
+	if (stat("/", &sb) == -1)
+		return NULL;
+
+	snprintf(buf, 32, "/sys/dev/block/%u:%u/uevent",
+		major(sb.st_dev), minor(sb.st_dev));
+
+	fd = open(buf, O_RDONLY);
+
+	if (fd < 0)
+		return NULL;
+
+	ret = lseek(fd, (off_t)0, SEEK_END);
+	(void)lseek(fd, (off_t)0, SEEK_SET);
+
+	if (ret == -1) {
+		close(fd);
+		return NULL;
+	}
+
+	uevent = malloc(ret + 1);
+	uevent[ret] = '\0';
+
+	ret = read(fd, uevent, ret);
+	close(fd);
+
+	ptr = strstr(uevent, "DEVNAME");
+	if (!ptr)
+		return NULL;
+
+	ret = sscanf(ptr, "DEVNAME=%s\n", buf);
+	snprintf(rootdev, PATH_MAX + 1, "/dev/%s", buf);
+
+	return rootdev;
+}
+
+/*
  * device information
  */
-void f2fs_init_configuration(struct f2fs_configuration *c)
+void f2fs_init_configuration(void)
 {
-	c->total_sectors = 0;
-	c->sector_size = DEFAULT_SECTOR_SIZE;
-	c->sectors_per_blk = DEFAULT_SECTORS_PER_BLOCK;
-	c->blks_per_seg = DEFAULT_BLOCKS_PER_SEGMENT;
+	int i;
+
+	c.ndevs = 1;
+	c.total_sectors = 0;
+	c.sector_size = 0;
+	c.sectors_per_blk = DEFAULT_SECTORS_PER_BLOCK;
+	c.blks_per_seg = DEFAULT_BLOCKS_PER_SEGMENT;
+	c.rootdev_name = get_rootdev();
+	c.wanted_total_sectors = -1;
+	c.zoned_mode = 0;
+	c.zoned_model = 0;
+	c.zone_blocks = 0;
+
+	for (i = 0; i < MAX_DEVICES; i++) {
+		memset(&c.devices[i], 0, sizeof(struct device_info));
+		c.devices[i].fd = -1;
+		c.devices[i].sector_size = DEFAULT_SECTOR_SIZE;
+		c.devices[i].end_blkaddr = -1;
+		c.devices[i].zoned_model = F2FS_ZONED_NONE;
+	}
 
 	/* calculated by overprovision ratio */
-	c->reserved_segments = 48;
-	c->overprovision = 5;
-	c->segs_per_sec = 1;
-	c->secs_per_zone = 1;
-	c->segs_per_zone = 1;
-	c->heap = 1;
-	c->vol_label = "";
-	c->device_name = NULL;
-	c->trim = 1;
+	c.reserved_segments = 0;
+	c.overprovision = 0;
+	c.segs_per_sec = 1;
+	c.secs_per_zone = 1;
+	c.segs_per_zone = 1;
+	c.heap = 1;
+	c.vol_label = "";
+	c.trim = 1;
+	c.ro = 0;
+	c.kd = -1;
 }
 
 static int is_mounted(const char *mpt, const char *device)
@@ -374,40 +585,62 @@
 		return 0;
 
 	while ((mnt = getmntent(file)) != NULL) {
-		if (!strcmp(device, mnt->mnt_fsname))
+		if (!strcmp(device, mnt->mnt_fsname)) {
+#ifdef MNTOPT_RO
+			if (hasmntopt(mnt, MNTOPT_RO))
+				c.ro = 1;
+#endif
 			break;
+		}
 	}
 	endmntent(file);
 	return mnt ? 1 : 0;
 }
 
-int f2fs_dev_is_umounted(struct f2fs_configuration *c)
+int f2fs_dev_is_umounted(char *path)
 {
 	struct stat st_buf;
+	int is_rootdev = 0;
 	int ret = 0;
 
-	ret = is_mounted(MOUNTED, c->device_name);
+	if (c.rootdev_name && !strcmp(path, c.rootdev_name))
+		is_rootdev = 1;
+
+	/*
+	 * try with /proc/mounts fist to detect RDONLY.
+	 * f2fs_stop_checkpoint makes RO in /proc/mounts while RW in /etc/mtab.
+	 */
+	ret = is_mounted("/proc/mounts", path);
 	if (ret) {
-		MSG(0, "\tError: Not available on mounted device!\n");
+		MSG(0, "Info: Mounted device!\n");
+		return -1;
+	}
+
+	ret = is_mounted(MOUNTED, path);
+	if (ret) {
+		MSG(0, "Info: Mounted device!\n");
 		return -1;
 	}
 
 	/*
-	 * if failed due to /etc/mtab file not present
-	 * try with /proc/mounts.
+	 * If we are supposed to operate on the root device, then
+	 * also check the mounts for '/dev/root', which sometimes
+	 * functions as an alias for the root device.
 	 */
-	ret = is_mounted("/proc/mounts", c->device_name);
-	if (ret) {
-		MSG(0, "\tError: Not available on mounted device!\n");
-		return -1;
+	if (is_rootdev) {
+		ret = is_mounted("/proc/mounts", "/dev/root");
+		if (ret) {
+			MSG(0, "Info: Mounted device!\n");
+			return -1;
+		}
 	}
 
 	/*
 	 * If f2fs is umounted with -l, the process can still use
 	 * the file system. In this case, we should not format.
 	 */
-	if (stat(c->device_name, &st_buf) == 0 && S_ISBLK(st_buf.st_mode)) {
-		int fd = open(c->device_name, O_RDONLY | O_EXCL);
+	if (stat(path, &st_buf) == 0 && S_ISBLK(st_buf.st_mode)) {
+		int fd = open(path, O_RDONLY | O_EXCL);
 
 		if (fd >= 0) {
 			close(fd);
@@ -419,6 +652,16 @@
 	return 0;
 }
 
+int f2fs_devs_are_umounted(void)
+{
+	int i;
+
+	for (i = 0; i < c.ndevs; i++)
+		if (f2fs_dev_is_umounted((char *)c.devices[i].path))
+			return -1;
+	return 0;
+}
+
 void get_kernel_version(__u8 *version)
 {
 	int i;
@@ -429,7 +672,7 @@
 	memset(version + i, 0, VERSION_LEN + 1 - i);
 }
 
-int f2fs_get_device_info(struct f2fs_configuration *c)
+int get_device_info(int i)
 {
 	int32_t fd = 0;
 	uint32_t sector_size;
@@ -438,18 +681,28 @@
 #endif
 	struct stat stat_buf;
 	struct hd_geometry geom;
-	u_int64_t wanted_total_sectors = c->total_sectors;
+#ifndef WITH_ANDROID
+	sg_io_hdr_t io_hdr;
+	unsigned char reply_buffer[96] = {0};
+	unsigned char model_inq[6] = {MODELINQUIRY};
+#endif
+	struct device_info *dev = c.devices + i;
 
-	fd = open(c->device_name, O_RDWR);
+	fd = open((char *)dev->path, O_RDWR);
 	if (fd < 0) {
 		MSG(0, "\tError: Failed to open the device!\n");
 		return -1;
 	}
-	c->fd = fd;
 
-	c->kd = open("/proc/version", O_RDONLY);
-	if (c->kd < 0)
-		MSG(0, "\tInfo: No support kernel version!\n");
+	dev->fd = fd;
+
+	if (c.kd == -1) {
+		c.kd = open("/proc/version", O_RDONLY);
+		if (c.kd < 0) {
+			MSG(0, "\tInfo: No support kernel version!\n");
+			c.kd = -2;
+		}
+	}
 
 	if (fstat(fd, &stat_buf) < 0 ) {
 		MSG(0, "\tError: Failed to get the device stat!\n");
@@ -457,55 +710,144 @@
 	}
 
 	if (S_ISREG(stat_buf.st_mode)) {
-		c->total_sectors = stat_buf.st_size / c->sector_size;
+		dev->total_sectors = stat_buf.st_size / dev->sector_size;
 	} else if (S_ISBLK(stat_buf.st_mode)) {
-		if (ioctl(fd, BLKSSZGET, &sector_size) < 0) {
+		if (ioctl(fd, BLKSSZGET, &sector_size) < 0)
 			MSG(0, "\tError: Using the default sector size\n");
-		} else {
-			if (c->sector_size < sector_size) {
-				c->sector_size = sector_size;
-				c->sectors_per_blk = PAGE_SIZE / sector_size;
-			}
-		}
-
+		else if (dev->sector_size < sector_size)
+			dev->sector_size = sector_size;
 #ifdef BLKGETSIZE64
-		if (ioctl(fd, BLKGETSIZE64, &c->total_sectors) < 0) {
+		if (ioctl(fd, BLKGETSIZE64, &dev->total_sectors) < 0) {
 			MSG(0, "\tError: Cannot get the device size\n");
 			return -1;
 		}
-		c->total_sectors /= c->sector_size;
 #else
 		if (ioctl(fd, BLKGETSIZE, &total_sectors) < 0) {
 			MSG(0, "\tError: Cannot get the device size\n");
 			return -1;
 		}
-		total_sectors /= c->sector_size;
-		c->total_sectors = total_sectors;
+		dev->total_sectors = total_sectors;
 #endif
+		dev->total_sectors /= dev->sector_size;
+
 		if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
-			c->start_sector = 0;
+			c.start_sector = 0;
 		else
-			c->start_sector = geom.start;
+			c.start_sector = geom.start;
+
+#ifndef WITH_ANDROID
+		/* Send INQUIRY command */
+		memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+		io_hdr.interface_id = 'S';
+		io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+		io_hdr.dxfer_len = sizeof(reply_buffer);
+		io_hdr.dxferp = reply_buffer;
+		io_hdr.cmd_len = sizeof(model_inq);
+		io_hdr.cmdp = model_inq;
+		io_hdr.timeout = 1000;
+
+		if (!ioctl(fd, SG_IO, &io_hdr)) {
+			int i = 16;
+
+			MSG(0, "Info: [%s] Disk Model: ",
+					dev->path);
+			while (reply_buffer[i] != '`' && i < 80)
+				printf("%c", reply_buffer[i++]);
+			printf("\n");
+		}
+#endif
 	} else {
 		MSG(0, "\tError: Volume type is not supported!!!\n");
 		return -1;
 	}
-	if (wanted_total_sectors && wanted_total_sectors < c->total_sectors) {
-		MSG(0, "Info: total device sectors = %"PRIu64" (in %u bytes)\n",
-					c->total_sectors, c->sector_size);
-		c->total_sectors = wanted_total_sectors;
 
-	}
-	MSG(0, "Info: sector size = %u\n", c->sector_size);
-	MSG(0, "Info: total sectors = %"PRIu64" (in %u bytes)\n",
-					c->total_sectors, c->sector_size);
-	if (c->total_sectors <
-			(F2FS_MIN_VOLUME_SIZE / c->sector_size)) {
-		MSG(0, "Error: Min volume size supported is %d\n",
-				F2FS_MIN_VOLUME_SIZE);
+	if (!c.sector_size) {
+		c.sector_size = dev->sector_size;
+		c.sectors_per_blk = F2FS_BLKSIZE / c.sector_size;
+	} else if (c.sector_size != c.devices[i].sector_size) {
+		MSG(0, "\tError: Different sector sizes!!!\n");
 		return -1;
 	}
 
+#ifndef WITH_ANDROID
+	if (S_ISBLK(stat_buf.st_mode))
+		f2fs_get_zoned_model(i);
+
+	if (dev->zoned_model != F2FS_ZONED_NONE) {
+		if (dev->zoned_model == F2FS_ZONED_HM)
+			c.zoned_model = F2FS_ZONED_HM;
+
+		if (f2fs_get_zone_blocks(i)) {
+			MSG(0, "\tError: Failed to get number of blocks per zone\n");
+			return -1;
+		}
+
+		if (f2fs_check_zones(i)) {
+			MSG(0, "\tError: Failed to check zone configuration\n");
+			return -1;
+		}
+		MSG(0, "Info: Host-%s zoned block device:\n",
+				(dev->zoned_model == F2FS_ZONED_HA) ?
+					"aware" : "managed");
+		MSG(0, "      %u zones, %u randomly writeable zones\n",
+				dev->nr_zones, dev->nr_rnd_zones);
+		MSG(0, "      %lu blocks per zone\n",
+				dev->zone_blocks);
+	}
+#endif
+	c.total_sectors += dev->total_sectors;
 	return 0;
 }
 
+int f2fs_get_device_info(void)
+{
+	int i;
+
+	for (i = 0; i < c.ndevs; i++)
+		if (get_device_info(i))
+			return -1;
+
+	if (c.wanted_total_sectors < c.total_sectors) {
+		MSG(0, "Info: total device sectors = %"PRIu64" (in %u bytes)\n",
+				c.total_sectors, c.sector_size);
+		c.total_sectors = c.wanted_total_sectors;
+		c.devices[0].total_sectors = c.total_sectors;
+	}
+	if (c.total_sectors * c.sector_size >
+		(u_int64_t)F2FS_MAX_SEGMENT * 2 * 1024 * 1024) {
+		MSG(0, "\tError: F2FS can support 16TB at most!!!\n");
+		return -1;
+	}
+
+	for (i = 0; i < c.ndevs; i++) {
+		if (c.devices[i].zoned_model != F2FS_ZONED_NONE) {
+			if (c.zone_blocks &&
+				c.zone_blocks != c.devices[i].zone_blocks) {
+				MSG(0, "\tError: not support different zone sizes!!!\n");
+				return -1;
+			}
+			c.zone_blocks = c.devices[i].zone_blocks;
+		}
+	}
+
+	/*
+	 * Align sections to the device zone size
+	 * and align F2FS zones to the device zones.
+	 */
+	if (c.zone_blocks) {
+		c.segs_per_sec = c.zone_blocks / DEFAULT_BLOCKS_PER_SEGMENT;
+		c.secs_per_zone = 1;
+	} else {
+		c.zoned_mode = 0;
+	}
+
+	c.segs_per_zone = c.segs_per_sec * c.secs_per_zone;
+
+	MSG(0, "Info: Segments per section = %d\n", c.segs_per_sec);
+	MSG(0, "Info: Sections per zone = %d\n", c.secs_per_zone);
+	MSG(0, "Info: sector size = %u\n", c.sector_size);
+	MSG(0, "Info: total sectors = %"PRIu64" (%"PRIu64" MB)\n",
+				c.total_sectors, (c.total_sectors *
+					(c.sector_size >> 9)) >> 11);
+	return 0;
+}
diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
index afa345f..c09db36 100644
--- a/lib/libf2fs_io.c
+++ b/lib/libf2fs_io.c
@@ -23,33 +23,58 @@
 
 #include <f2fs_fs.h>
 
-struct f2fs_configuration config;
+struct f2fs_configuration c;
+
+static int __get_device_fd(__u64 *offset)
+{
+	__u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS;
+	int i;
+
+	for (i = 0; i < c.ndevs; i++) {
+		if (c.devices[i].start_blkaddr <= blk_addr &&
+				c.devices[i].end_blkaddr >= blk_addr) {
+			*offset -=
+				c.devices[i].start_blkaddr << F2FS_BLKSIZE_BITS;
+			return c.devices[i].fd;
+		}
+	}
+	return -1;
+}
 
 /*
  * IO interfaces
  */
 int dev_read_version(void *buf, __u64 offset, size_t len)
 {
-	if (lseek64(config.kd, (off64_t)offset, SEEK_SET) < 0)
+	if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0)
 		return -1;
-	if (read(config.kd, buf, len) < 0)
+	if (read(c.kd, buf, len) < 0)
 		return -1;
 	return 0;
 }
 
 int dev_read(void *buf, __u64 offset, size_t len)
 {
-	if (lseek64(config.fd, (off64_t)offset, SEEK_SET) < 0)
+	int fd = __get_device_fd(&offset);
+
+	if (fd < 0)
+		return fd;
+
+	if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
 		return -1;
-	if (read(config.fd, buf, len) < 0)
+	if (read(fd, buf, len) < 0)
 		return -1;
 	return 0;
 }
 
 int dev_readahead(__u64 offset, size_t len)
 {
+	int fd = __get_device_fd(&offset);
+
+	if (fd < 0)
+		return fd;
 #ifdef POSIX_FADV_WILLNEED
-	return posix_fadvise(config.fd, offset, len, POSIX_FADV_WILLNEED);
+	return posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED);
 #else
 	return 0;
 #endif
@@ -57,65 +82,78 @@
 
 int dev_write(void *buf, __u64 offset, size_t len)
 {
-	if (lseek64(config.fd, (off64_t)offset, SEEK_SET) < 0)
+	int fd = __get_device_fd(&offset);
+
+	if (fd < 0)
+		return fd;
+
+	if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
 		return -1;
-	if (write(config.fd, buf, len) < 0)
+	if (write(fd, buf, len) < 0)
 		return -1;
 	return 0;
 }
 
 int dev_write_block(void *buf, __u64 blk_addr)
 {
-	return dev_write(buf, blk_addr * F2FS_BLKSIZE, F2FS_BLKSIZE);
+	return dev_write(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
 }
 
 int dev_write_dump(void *buf, __u64 offset, size_t len)
 {
-	if (lseek64(config.dump_fd, (off64_t)offset, SEEK_SET) < 0)
+	if (lseek64(c.dump_fd, (off64_t)offset, SEEK_SET) < 0)
 		return -1;
-	if (write(config.dump_fd, buf, len) < 0)
+	if (write(c.dump_fd, buf, len) < 0)
 		return -1;
 	return 0;
 }
 
 int dev_fill(void *buf, __u64 offset, size_t len)
 {
+	int fd = __get_device_fd(&offset);
+
+	if (fd < 0)
+		return fd;
+
 	/* Only allow fill to zero */
 	if (*((__u8*)buf))
 		return -1;
-	if (lseek64(config.fd, (off64_t)offset, SEEK_SET) < 0)
+	if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
 		return -1;
-	if (write(config.fd, buf, len) < 0)
+	if (write(fd, buf, len) < 0)
 		return -1;
 	return 0;
 }
 
-int dev_read_block(void *buf, __u64 blk_addr)
+int dev_fill_block(void *buf, __u64 blk_addr)
 {
-	return dev_read(buf, blk_addr * F2FS_BLKSIZE, F2FS_BLKSIZE);
+	return dev_fill(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
 }
 
-int dev_read_blocks(void *buf, __u64 addr, __u32 nr_blks)
+int dev_read_block(void *buf, __u64 blk_addr)
 {
-	return dev_read(buf, addr * F2FS_BLKSIZE, nr_blks * F2FS_BLKSIZE);
+	return dev_read(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
 }
 
 int dev_reada_block(__u64 blk_addr)
 {
-	return dev_readahead(blk_addr * F2FS_BLKSIZE, F2FS_BLKSIZE);
+	return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
 }
 
-void f2fs_finalize_device(struct f2fs_configuration *c)
+void f2fs_finalize_device(void)
 {
+	int i;
+
 	/*
 	 * We should call fsync() to flush out all the dirty pages
 	 * in the block device page cache.
 	 */
-	if (fsync(c->fd) < 0)
-		MSG(0, "\tError: Could not conduct fsync!!!\n");
+	for (i = 0; i < c.ndevs; i++) {
+		if (fsync(c.devices[i].fd) < 0)
+			MSG(0, "\tError: Could not conduct fsync!!!\n");
 
-	if (close(c->fd) < 0)
-		MSG(0, "\tError: Failed to close device file!!!\n");
-
-	close(c->kd);
+		if (close(c.devices[i].fd) < 0)
+			MSG(0, "\tError: Failed to close device file!!!\n");
+	}
+	close(c.kd);
 }
diff --git a/lib/libf2fs_zoned.c b/lib/libf2fs_zoned.c
new file mode 100644
index 0000000..eebf030
--- /dev/null
+++ b/lib/libf2fs_zoned.c
@@ -0,0 +1,310 @@
+/**
+ * libf2fs_zoned.c
+ *
+ * Copyright (c) 2016 Western Digital Corporation.
+ * Written by: Damien Le Moal <damien.lemoal@wdc.com>
+ *
+ * Dual licensed under the GPL or LGPL version 2 licenses.
+ */
+#define _LARGEFILE64_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <libgen.h>
+
+#include <f2fs_fs.h>
+
+#ifdef HAVE_LINUX_BLKZONED_H
+
+void f2fs_get_zoned_model(int i)
+{
+	struct device_info *dev = c.devices + i;
+	char str[128];
+	FILE *file;
+	int res;
+
+	/* Check that this is a zoned block device */
+	snprintf(str, sizeof(str),
+		 "/sys/block/%s/queue/zoned",
+		 basename(dev->path));
+	file = fopen(str, "r");
+	if (!file)
+		goto not_zoned;
+
+	memset(str, 0, sizeof(str));
+	res = fscanf(file, "%s", str);
+	fclose(file);
+
+	if (res != 1)
+		goto not_zoned;
+
+	if (strcmp(str, "host-aware") == 0) {
+		dev->zoned_model = F2FS_ZONED_HA;
+		return;
+	}
+	if (strcmp(str, "host-managed") == 0) {
+		dev->zoned_model = F2FS_ZONED_HM;
+		return;
+	}
+
+not_zoned:
+	dev->zoned_model = F2FS_ZONED_NONE;
+}
+
+int f2fs_get_zone_blocks(int i)
+{
+	struct device_info *dev = c.devices + i;
+	uint64_t sectors;
+	char str[128];
+	FILE *file;
+	int res;
+
+	/* Get zone size */
+	dev->zone_blocks = 0;
+
+	snprintf(str, sizeof(str),
+		 "/sys/block/%s/queue/chunk_sectors",
+		 basename(dev->path));
+	file = fopen(str, "r");
+	if (!file)
+		return -1;
+
+	memset(str, 0, sizeof(str));
+	res = fscanf(file, "%s", str);
+	fclose(file);
+
+	if (res != 1)
+		return -1;
+
+	sectors = atol(str);
+	if (!sectors)
+		return -1;
+
+	dev->zone_blocks = sectors >> (F2FS_BLKSIZE_BITS - 9);
+	sectors = (sectors << 9) / c.sector_size;
+
+	/*
+	 * Total number of zones: there may
+	 * be a last smaller runt zone.
+	 */
+	dev->nr_zones = dev->total_sectors / sectors;
+	if (dev->total_sectors % sectors)
+		dev->nr_zones++;
+
+	return 0;
+}
+
+#define F2FS_REPORT_ZONES_BUFSZ	524288
+
+int f2fs_check_zones(int j)
+{
+	struct device_info *dev = c.devices + j;
+	struct blk_zone_report *rep;
+	struct blk_zone *blkz;
+	unsigned int i, n = 0;
+	u_int64_t total_sectors;
+	u_int64_t sector;
+	int last_is_conv = 1;
+	int ret = -1;
+
+	rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
+	if (!rep) {
+		ERR_MSG("No memory for report zones\n");
+		return -ENOMEM;
+	}
+
+	dev->nr_rnd_zones = 0;
+	sector = 0;
+	total_sectors = (dev->total_sectors * c.sector_size) >> 9;
+
+	while (sector < total_sectors) {
+
+		/* Get zone info */
+		memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
+		rep->sector = sector;
+		rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
+			/ sizeof(struct blk_zone);
+
+		ret = ioctl(dev->fd, BLKREPORTZONE, rep);
+		if (ret != 0) {
+			ret = -errno;
+			ERR_MSG("ioctl BLKREPORTZONE failed\n");
+			goto out;
+		}
+
+		if (!rep->nr_zones)
+			break;
+
+		blkz = (struct blk_zone *)(rep + 1);
+		for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
+
+			if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY ||
+			    blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE)
+				last_is_conv = 0;
+			if (blk_zone_conv(blkz) ||
+			    blk_zone_seq_pref(blkz)) {
+				if (last_is_conv)
+					dev->nr_rnd_zones++;
+			} else {
+				last_is_conv = 0;
+			}
+
+			if (blk_zone_conv(blkz)) {
+				DBG(2,
+				    "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n",
+				    n,
+				    blk_zone_cond(blkz),
+				    blk_zone_cond_str(blkz),
+				    blk_zone_sector(blkz),
+				    blk_zone_length(blkz));
+			} else {
+				DBG(2,
+				    "Zone %05u: type 0x%x (%s), cond 0x%x (%s), need_reset %d, "
+				    "non_seq %d, sector %llu, %llu sectors, wp sector %llu\n",
+				    n,
+				    blk_zone_type(blkz),
+				    blk_zone_type_str(blkz),
+				    blk_zone_cond(blkz),
+				    blk_zone_cond_str(blkz),
+				    blk_zone_need_reset(blkz),
+				    blk_zone_non_seq(blkz),
+				    blk_zone_sector(blkz),
+				    blk_zone_length(blkz),
+				    blk_zone_wp_sector(blkz));
+			}
+
+			sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
+			n++;
+			blkz++;
+		}
+	}
+
+	if (sector != total_sectors) {
+		ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n",
+			(unsigned long long)(sector << 9) / c.sector_size,
+			(unsigned long long)dev->total_sectors);
+		ret = -1;
+		goto out;
+	}
+
+	if (n != dev->nr_zones) {
+		ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n",
+			dev->nr_zones, n);
+		ret = -1;
+		goto out;
+	}
+
+	if (dev->zoned_model == F2FS_ZONED_HM &&
+			!dev->nr_rnd_zones) {
+		ERR_MSG("No conventional zone for super block\n");
+		ret = -1;
+	}
+out:
+	free(rep);
+	return ret;
+}
+
+int f2fs_reset_zones(int j)
+{
+	struct device_info *dev = c.devices + j;
+	struct blk_zone_report *rep;
+	struct blk_zone *blkz;
+	struct blk_zone_range range;
+	u_int64_t total_sectors;
+	u_int64_t sector;
+	unsigned int i;
+	int ret = -1;
+
+	rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
+	if (!rep) {
+		ERR_MSG("No memory for report zones\n");
+		return -1;
+	}
+
+	sector = 0;
+	total_sectors = (dev->total_sectors * c.sector_size) >> 9;
+	while (sector < total_sectors) {
+
+		/* Get zone info */
+		memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
+		rep->sector = sector;
+		rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
+			/ sizeof(struct blk_zone);
+
+		ret = ioctl(dev->fd, BLKREPORTZONE, rep);
+		if (ret != 0) {
+			ret = -errno;
+			ERR_MSG("ioctl BLKREPORTZONES failed\n");
+			goto out;
+		}
+
+		if (!rep->nr_zones)
+			break;
+
+		blkz = (struct blk_zone *)(rep + 1);
+		for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
+			if (blk_zone_seq(blkz) &&
+			    !blk_zone_empty(blkz)) {
+				/* Non empty sequential zone: reset */
+				range.sector = blk_zone_sector(blkz);
+				range.nr_sectors = blk_zone_length(blkz);
+				ret = ioctl(dev->fd, BLKRESETZONE, &range);
+				if (ret != 0) {
+					ret = -errno;
+					ERR_MSG("ioctl BLKRESETZONE failed\n");
+					goto out;
+				}
+			}
+			sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
+			blkz++;
+		}
+	}
+out:
+	free(rep);
+	if (!ret)
+		MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20);
+	return ret;
+}
+
+#else
+
+void f2fs_get_zoned_model(int i)
+{
+	struct device_info *dev = c.devices + i;
+
+	c.zoned_mode = 0;
+	dev->zoned_model = F2FS_ZONED_NONE;
+}
+
+int f2fs_get_zone_blocks(int i)
+{
+	struct device_info *dev = c.devices + i;
+
+	c.zoned_mode = 0;
+	dev->nr_zones = 0;
+	dev->zone_blocks = 0;
+	dev->zoned_model = F2FS_ZONED_NONE;
+
+	return 0;
+}
+
+int f2fs_check_zones(int i)
+{
+	ERR_MSG("%d: Zoned block devices are not supported\n", i);
+	return -1;
+}
+
+int f2fs_reset_zones(int i)
+{
+	ERR_MSG("%d: Zoned block devices are not supported\n", i);
+	return -1;
+}
+
+#endif
+
diff --git a/man/Makefile.am b/man/Makefile.am
index 477d654..7856586 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,3 +1,3 @@
 ## Makefile.am
 
-dist_man_MANS = mkfs.f2fs.8
+dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8
diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
new file mode 100644
index 0000000..b08399b
--- /dev/null
+++ b/man/defrag.f2fs.8
@@ -0,0 +1,76 @@
+.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+.\"
+.TH DEFRAG.F2FS 8
+.SH NAME
+defrag.f2fs \- relocate blocks in a given area to the specified region
+.SH SYNOPSIS
+.B defrag.f2fs
+[
+.B \-s
+.I start block address
+]
+[
+.B \-l
+.I number of blocks
+]
+[
+.B \-t
+.I target block address
+]
+[
+.B \-i
+.I direction
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B defrag.f2fs
+is used to move specified number of blocks starting from a given block address
+to the target block address with a direction.
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+For example,
+# defrag.f2fs -s 0x4000 -l 0x100 -t 0x10000 -i /dev/sdb1
+
+This moves blocks between 0x4000 and 0x4100 to the left-hand area of 0x10000.
+
+.PP
+The exit code returned by
+.B defrag.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-s " start block address"
+Specify the starting block address.
+.TP
+.BI \-l " number of blocks"
+Specifiy the number of blocks to move.
+.TP
+.BI \-t " target block address"
+Specify the destination block address.
+.TP
+.BI \-i " direction"
+Set the direction to left. If it is not set, the direction becomes right
+by default.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+This version of
+.B defrag.f2fs
+has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
+.SH AVAILABILITY
+.B defrag.f2fs
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+.SH SEE ALSO
+.BR mkfs.f2fs(8),
+.BR dump.f2fs(8),
+.BR fsck.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
new file mode 100644
index 0000000..35616e5
--- /dev/null
+++ b/man/dump.f2fs.8
@@ -0,0 +1,70 @@
+.\" Copyright (c) 2013 Samsung Electronics Co., Ltd.
+.\"
+.TH DUMP.F2FS 8
+.SH NAME
+dump.f2fs \- retrieve directory and file entries from an F2FS-formated image
+.SH SYNOPSIS
+.B dump.f2fs
+[
+.B \-i
+.I inode number
+]
+[
+.B \-s
+.I SIT range
+]
+[
+.B \-a
+.I SSA range
+]
+[
+.B \-b
+.I block address
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B dump.f2fs
+is used to retrieve f2fs metadata (usually in a disk partition).
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+Currently, it can retrieve 1) a file given its inode number, 2) SIT entries into
+a file, 3) SSA entries into a file, 4) reverse information from the given block
+address.
+.PP
+The exit code returned by
+.B dump.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-i " inode number"
+Specify an inode number to dump out.
+.TP
+.BI \-s " SIT range"
+Specify a range presented by segment numbers to dump SIT entries.
+.TP
+.BI \-a " SSA range"
+Specify a range presented by segment numbers to dump SSA entries.
+.TP
+.BI \-b " block address"
+Specify a block address to retrieve its metadata information.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+Initial checking code was written by Byoung Geun Kim <bgbg.kim@samsung.com>.
+.SH AVAILABILITY
+.B dump.f2fs
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+.SH SEE ALSO
+.BR mkfs.f2fs(8),
+.BR fsck.f2fs(8),
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
new file mode 100644
index 0000000..af1076c
--- /dev/null
+++ b/man/fsck.f2fs.8
@@ -0,0 +1,69 @@
+.\" Copyright (c) 2013 Samsung Electronics Co., Ltd.
+.\"
+.TH FSCK.F2FS 8
+.SH NAME
+fsck.f2fs \- check a Linux F2FS file system
+.SH SYNOPSIS
+.B fsck.f2fs
+[
+.B \-a
+.I enable auto fix
+]
+[
+.B \-f
+.I enable force fix
+]
+[
+.B \-p
+.I enable preen mode
+]
+[
+.B \-t
+.I show stored directory tree
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B fsck.f2fs
+is used to check an f2fs file system (usually in a disk partition).
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+.PP
+The exit code returned by
+.B fsck.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-a " enable auto fix"
+Enable to run file system check only if a bug was reported by the F2FS kernel
+module. It is disabled by default.
+.TP
+.BI \-f " enable force fix"
+Enable to fix all the inconsistency in the partition.
+.TP
+.BI \-p " enable preen mode"
+Same as "-a" to support general fsck convention.
+.TP
+.BI \-t " show stored directory tree"
+Enable to show every directory entries in the partition.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+Initial checking code was written by Byoung Geun Kim <bgbg.kim@samsung.com>.
+Jaegeuk Kim <jaegeuk@kernel.org> reworked most parts of the codes to support
+fixing any corrupted images.
+.SH AVAILABILITY
+.B fsck.f2fs
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+.SH SEE ALSO
+.BR mkfs.f2fs(8),
+.BR dump.f2fs(8),
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
index f386ac6..bdb9755 100644
--- a/man/mkfs.f2fs.8
+++ b/man/mkfs.f2fs.8
@@ -2,7 +2,7 @@
 .\"             http://www.samsung.com/
 .\"  Written by Jaegeuk Kim <jaegeuk.kim@samsung.com>
 .\"
-.TH MKFS.F2FS 8 "January 2013" "f2fs-tools version 1.2.0"
+.TH MKFS.F2FS 8
 .SH NAME
 mkfs.f2fs \- create an F2FS file system
 .SH SYNOPSIS
@@ -12,6 +12,10 @@
 .I heap-based-allocation
 ]
 [
+.B \-c
+.I device
+]
+[
 .B \-l
 .I volume-label
 ]
@@ -21,7 +25,7 @@
 ]
 [
 .B \-s
-.I log-based-#-of-segments-per-section
+.I #-of-segments-per-section
 ]
 [
 .B \-z
@@ -36,11 +40,13 @@
 .I debugging-level
 ]
 .I device
+.I [sectors]
 .SH DESCRIPTION
 .B mkfs.f2fs
 is used to create a f2fs file system (usually in a disk partition).
 \fIdevice\fP is the special file corresponding to the device (e.g.
 \fI/dev/sdXX\fP).
+\fIsectors\fP is optionally given for specifing the filesystem size.
 .PP
 The exit code returned by
 .B mkfs.f2fs
@@ -53,17 +59,22 @@
 assigned separately according to the whole volume size.
 The default value is 1.
 .TP
+.BI \-c " device"
+Build f2fs with this device additionally, so that user can see all
+the devices as one big volume.
+.TP
 .BI \-l " volume-label"
 Specify the volume label to the partition mounted as F2FS.
 .TP
 .BI \-o " overprovision-ratio-percentage"
 Specify the percentage over the volume size for overprovision area. This area
-is hidden to users, and utilized by F2FS cleaner. The default percentage is 5%.
+is hidden to users, and utilized by F2FS cleaner. If not specified, the best
+number will be assigned automatically accoring to the partition size.
 .TP
-.BI \-s " log-based-#-of-segments-per-section"
-Specify the log-based number of segments per section. A section consists of
+.BI \-s " #-of-segments-per-section"
+Specify the number of segments per section. A section consists of
 multiple consecutive segments, and is the unit of garbage collection.
-The default number is 0, which means one segment is assigned to a section.
+The default number is 1, which means one segment is assigned to a section.
 .TP
 .BI \-z " #-of-sections-per-zone"
 Specify the number of sections per zone. A zone consists of multiple sections.
@@ -88,4 +99,9 @@
 .B mkfs.f2fs
 is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
 .SH SEE ALSO
-.BR mkfs (8).
+.BR mkfs (8),
+.BR fsck.f2fs(8),
+.BR dump.f2fs(8),
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
new file mode 100644
index 0000000..463eca5
--- /dev/null
+++ b/man/resize.f2fs.8
@@ -0,0 +1,50 @@
+.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+.\"
+.TH RESIZE.F2FS 8
+.SH NAME
+resize.f2fs \- resize filesystem size
+.SH SYNOPSIS
+.B resize.f2fs
+[
+.B \-t
+.I target sectors
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B resize.f2fs
+is used to resize an f2fs file system (usually in a disk partition).
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+Current version only supports expanding the prebuilt filesystem.
+
+.PP
+The exit code returned by
+.B resize.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-t " target sectors"
+Specify the size in sectors.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+This version of
+.B resize.f2fs
+has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
+.SH AVAILABILITY
+.B resize.f2fs
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+.SH SEE ALSO
+.BR mkfs.f2fs(8),
+.BR fsck.f2fs(8),
+.BR dump.f2fs(8),
+.BR defrag.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/sload.f2fs.8 b/man/sload.f2fs.8
new file mode 100644
index 0000000..d07330c
--- /dev/null
+++ b/man/sload.f2fs.8
@@ -0,0 +1,56 @@
+.\" Copyright (C) 2015 Huawei Ltd.
+.\"
+.TH SLOAD.F2FS 8
+.SH NAME
+sload.f2fs \- load directories and files into the device directly
+.SH SYNOPSIS
+.B sload.f2fs
+[
+.B \-f
+.I source directory path
+]
+[
+.B \-t
+.I mount point
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B sload.f2fs
+is used to load directories and files into a disk partition.
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+.PP
+The exit code returned by
+.B sload.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-f " source directory path"
+Specify the source directory path to be loaded.
+.TP
+.BI \-t " mount point path"
+Specify the mount point path in the partition to load.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+This version of
+.B sload.f2fs
+has been written by Hou Pengyang <houpengyang@huawei.com>,
+Liu Shuoran <liushuoran@huawei.com>, Jaegeuk Kim <jaegeuk@kernel.org>
+.SH AVAILABILITY
+.B sload.f2fs
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+.SH SEE ALSO
+.BR mkfs.f2fs(8),
+.BR fsck.f2fs(8),
+.BR dump.f2fs(8),
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8).
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index fa48699..8b4c16c 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -3,5 +3,12 @@
 AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
 AM_CFLAGS = -Wall -DWITH_BLKDISCARD
 sbin_PROGRAMS = mkfs.f2fs
-mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c
+mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c f2fs_format_utils.h $(top_srcdir)/include/f2fs_fs.h
 mkfs_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+
+lib_LTLIBRARIES = libf2fs_format.la
+libf2fs_format_la_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c
+libf2fs_format_la_CFLAGS = -DWITH_BLKDISCARD
+libf2fs_format_la_CPPFLAGS = -I$(top_srcdir)/include
+libf2fs_format_la_LDFLAGS = -luuid -L$(top_srcdir)/lib -lf2fs \
+	-version-info $(FMT_CURRENT):$(FMT_REVISION):$(FMT_AGE)
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index 094afa3..3c13026 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -21,72 +21,16 @@
 #include "f2fs_fs.h"
 #include "f2fs_format_utils.h"
 
-extern struct f2fs_configuration config;
-struct f2fs_super_block sb;
+extern struct f2fs_configuration c;
+struct f2fs_super_block raw_sb;
+struct f2fs_super_block *sb = &raw_sb;
 struct f2fs_checkpoint *cp;
 
 /* Return first segment number of each area */
-#define prev_zone(cur)		(config.cur_seg[cur] - config.segs_per_zone)
-#define next_zone(cur)		(config.cur_seg[cur] + config.segs_per_zone)
-#define last_zone(cur)		((cur - 1) * config.segs_per_zone)
-#define last_section(cur)	(cur + (config.secs_per_zone - 1) * config.segs_per_sec)
-
-#define set_sb_le64(member, val)		(sb.member = cpu_to_le64(val))
-#define set_sb_le32(member, val)		(sb.member = cpu_to_le32(val))
-#define set_sb_le16(member, val)		(sb.member = cpu_to_le16(val))
-#define get_sb_le64(member)			le64_to_cpu(sb.member)
-#define get_sb_le32(member)			le32_to_cpu(sb.member)
-#define get_sb_le16(member)			le16_to_cpu(sb.member)
-
-#define set_sb(member, val)	\
-			do {						\
-				typeof(sb.member) t;			\
-				switch (sizeof(t)) {			\
-				case 8: set_sb_le64(member, val); break; \
-				case 4: set_sb_le32(member, val); break; \
-				case 2: set_sb_le16(member, val); break; \
-				} \
-			} while(0)
-
-#define get_sb(member)		\
-			({						\
-				typeof(sb.member) t;			\
-				switch (sizeof(t)) {			\
-				case 8: t = get_sb_le64(member); break; \
-				case 4: t = get_sb_le32(member); break; \
-				case 2: t = get_sb_le16(member); break; \
-				} 					\
-				t; \
-			})
-
-#define set_cp_le64(member, val)		(cp->member = cpu_to_le64(val))
-#define set_cp_le32(member, val)		(cp->member = cpu_to_le32(val))
-#define set_cp_le16(member, val)		(cp->member = cpu_to_le16(val))
-#define get_cp_le64(member)			le64_to_cpu(cp->member)
-#define get_cp_le32(member)			le32_to_cpu(cp->member)
-#define get_cp_le16(member)			le16_to_cpu(cp->member)
-
-#define set_cp(member, val)	\
-			do {						\
-				typeof(cp->member) t;			\
-				switch (sizeof(t)) {			\
-				case 8: set_cp_le64(member, val); break; \
-				case 4: set_cp_le32(member, val); break; \
-				case 2: set_cp_le16(member, val); break; \
-				} \
-			} while(0)
-
-#define get_cp(member)		\
-			({						\
-				typeof(cp->member) t;			\
-				switch (sizeof(t)) {			\
-				case 8: t = get_cp_le64(member); break; \
-				case 4: t = get_cp_le32(member); break; \
-				case 2: t = get_cp_le16(member); break; \
-				} 					\
-				t; \
-			})
-
+#define prev_zone(cur)		(c.cur_seg[cur] - c.segs_per_zone)
+#define next_zone(cur)		(c.cur_seg[cur] + c.segs_per_zone)
+#define last_zone(cur)		((cur - 1) * c.segs_per_zone)
+#define last_section(cur)	(cur + (c.secs_per_zone - 1) * c.segs_per_sec)
 
 const char *media_ext_lists[] = {
 	"jpg",
@@ -118,21 +62,33 @@
 	NULL
 };
 
-static void configure_extension_list(void)
+static bool is_extension_exist(const char *name)
+{
+	int i;
+
+	for (i = 0; i < F2FS_MAX_EXTENSION; i++) {
+		char *ext = (char *)sb->extension_list[i];
+		if (!strcmp(ext, name))
+			return 1;
+	}
+
+	return 0;
+}
+
+static void cure_extension_list(void)
 {
 	const char **extlist = media_ext_lists;
-	char *ext_str = config.extension_list;
+	char *ext_str = c.extension_list;
 	char *ue;
 	int name_len;
 	int i = 0;
 
-	sb.extension_count = 0;
-	memset(sb.extension_list, 0,
-			sizeof(sb.extension_list));
+	set_sb(extension_count, 0);
+	memset(sb->extension_list, 0, sizeof(sb->extension_list));
 
 	while (*extlist) {
 		name_len = strlen(*extlist);
-		memcpy(sb.extension_list[i++], *extlist, name_len);
+		memcpy(sb->extension_list[i++], *extlist, name_len);
 		extlist++;
 	}
 	set_sb(extension_count, i);
@@ -141,18 +97,24 @@
 		return;
 
 	/* add user ext list */
-	ue = strtok(ext_str, ",");
+	ue = strtok(ext_str, ", ");
 	while (ue != NULL) {
 		name_len = strlen(ue);
-		memcpy(sb.extension_list[i++], ue, name_len);
-		ue = strtok(NULL, ",");
+		if (name_len >= 8) {
+			MSG(0, "\tWarn: Extension name (%s) is too long\n", ue);
+			goto next;
+		}
+		if (!is_extension_exist(ue))
+			memcpy(sb->extension_list[i++], ue, name_len);
+next:
+		ue = strtok(NULL, ", ");
 		if (i >= F2FS_MAX_EXTENSION)
 			break;
 	}
 
 	set_sb(extension_count, i);
 
-	free(config.extension_list);
+	free(c.extension_list);
 }
 
 static int f2fs_prepare_super_block(void)
@@ -164,19 +126,21 @@
 	u_int32_t sit_segments;
 	u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
 	u_int32_t total_valid_blks_available;
-	u_int64_t zone_align_start_offset, diff, total_meta_segments;
+	u_int64_t zone_align_start_offset, diff;
+	u_int64_t total_meta_zones, total_meta_segments;
 	u_int32_t sit_bitmap_size, max_sit_bitmap_size;
 	u_int32_t max_nat_bitmap_size, max_nat_segments;
 	u_int32_t total_zones;
+	int i;
 
 	set_sb(magic, F2FS_SUPER_MAGIC);
 	set_sb(major_ver, F2FS_MAJOR_VERSION);
 	set_sb(minor_ver, F2FS_MINOR_VERSION);
 
-	log_sectorsize = log_base_2(config.sector_size);
-	log_sectors_per_block = log_base_2(config.sectors_per_blk);
+	log_sectorsize = log_base_2(c.sector_size);
+	log_sectors_per_block = log_base_2(c.sectors_per_blk);
 	log_blocksize = log_sectorsize + log_sectors_per_block;
-	log_blks_per_seg = log_base_2(config.blks_per_seg);
+	log_blks_per_seg = log_base_2(c.blks_per_seg);
 
 	set_sb(log_sectorsize, log_sectorsize);
 	set_sb(log_sectors_per_block, log_sectors_per_block);
@@ -184,45 +148,83 @@
 	set_sb(log_blocksize, log_blocksize);
 	set_sb(log_blocks_per_seg, log_blks_per_seg);
 
-	set_sb(segs_per_sec, config.segs_per_sec);
-	set_sb(secs_per_zone, config.secs_per_zone);
+	set_sb(segs_per_sec, c.segs_per_sec);
+	set_sb(secs_per_zone, c.secs_per_zone);
 
 	blk_size_bytes = 1 << log_blocksize;
-	segment_size_bytes = blk_size_bytes * config.blks_per_seg;
+	segment_size_bytes = blk_size_bytes * c.blks_per_seg;
 	zone_size_bytes =
-		blk_size_bytes * config.secs_per_zone *
-		config.segs_per_sec * config.blks_per_seg;
+		blk_size_bytes * c.secs_per_zone *
+		c.segs_per_sec * c.blks_per_seg;
 
-	sb.checksum_offset = 0;
+	set_sb(checksum_offset, 0);
 
-	set_sb(block_count, config.total_sectors >> log_sectors_per_block);
+	set_sb(block_count, c.total_sectors >> log_sectors_per_block);
 
 	zone_align_start_offset =
-		(config.start_sector * config.sector_size +
+		(c.start_sector * c.sector_size +
 		2 * F2FS_BLKSIZE + zone_size_bytes - 1) /
 		zone_size_bytes * zone_size_bytes -
-		config.start_sector * config.sector_size;
+		c.start_sector * c.sector_size;
 
-	if (config.start_sector % config.sectors_per_blk) {
-		MSG(1, "\tWARN: Align start sector number to the page unit\n");
+	if (c.start_sector % c.sectors_per_blk) {
+		MSG(1, "\t%s: Align start sector number to the page unit\n",
+				c.zoned_mode ? "FAIL" : "WARN");
 		MSG(1, "\ti.e., start sector: %d, ofs:%d (sects/page: %d)\n",
-				config.start_sector,
-				config.start_sector % config.sectors_per_blk,
-				config.sectors_per_blk);
+				c.start_sector,
+				c.start_sector % c.sectors_per_blk,
+				c.sectors_per_blk);
+		if (c.zoned_mode)
+			return -1;
 	}
 
-	set_sb(segment_count, (config.total_sectors * config.sector_size -
-				zone_align_start_offset) / segment_size_bytes);
-
 	set_sb(segment0_blkaddr, zone_align_start_offset / blk_size_bytes);
-	sb.cp_blkaddr = sb.segment0_blkaddr;
+	sb->cp_blkaddr = sb->segment0_blkaddr;
 
-	MSG(0, "Info: zone aligned segment0 blkaddr: %u\n", get_sb(segment0_blkaddr));
+	MSG(0, "Info: zone aligned segment0 blkaddr: %u\n",
+					get_sb(segment0_blkaddr));
 
+	if (c.zoned_mode && (get_sb(segment0_blkaddr) + c.start_sector /
+					c.sectors_per_blk) % c.zone_blocks) {
+		MSG(1, "\tError: Unaligned segment0 block address %u\n",
+				get_sb(segment0_blkaddr));
+		return -1;
+	}
+
+	for (i = 0; i < c.ndevs; i++) {
+		if (i == 0) {
+			c.devices[i].total_segments =
+				(c.devices[i].total_sectors *
+				c.sector_size - zone_align_start_offset) /
+				segment_size_bytes;
+			c.devices[i].start_blkaddr = 0;
+			c.devices[i].end_blkaddr = c.devices[i].total_segments *
+						c.blks_per_seg - 1 +
+						sb->segment0_blkaddr;
+		} else {
+			c.devices[i].total_segments =
+				c.devices[i].total_sectors /
+				(c.sectors_per_blk * c.blks_per_seg);
+			c.devices[i].start_blkaddr =
+					c.devices[i - 1].end_blkaddr + 1;
+			c.devices[i].end_blkaddr = c.devices[i].start_blkaddr +
+					c.devices[i].total_segments *
+					c.blks_per_seg - 1;
+		}
+		if (c.ndevs > 1) {
+			memcpy(sb->devs[i].path, c.devices[i].path, MAX_PATH_LEN);
+			sb->devs[i].total_segments =
+					cpu_to_le32(c.devices[i].total_segments);
+		}
+
+		c.total_segments += c.devices[i].total_segments;
+	}
+	set_sb(segment_count, (c.total_segments / c.segs_per_zone *
+						c.segs_per_zone));
 	set_sb(segment_count_ckpt, F2FS_NUMBER_OF_CHECKPOINT_PACK);
 
-	set_sb(sit_blkaddr, get_sb(segment0_blkaddr) + get_sb(segment_count_ckpt) *
-			config.blks_per_seg);
+	set_sb(sit_blkaddr, get_sb(segment0_blkaddr) +
+			get_sb(segment_count_ckpt) * c.blks_per_seg);
 
 	blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
 
@@ -231,13 +233,14 @@
 	set_sb(segment_count_sit, sit_segments * 2);
 
 	set_sb(nat_blkaddr, get_sb(sit_blkaddr) + get_sb(segment_count_sit) *
-			config.blks_per_seg);
+			c.blks_per_seg);
 
 	total_valid_blks_available = (get_sb(segment_count) -
-			(get_sb(segment_count_ckpt) + get_sb(segment_count_sit))) *
-			config.blks_per_seg;
+			(get_sb(segment_count_ckpt) +
+			get_sb(segment_count_sit))) * c.blks_per_seg;
 
-	blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
+	blocks_for_nat = ALIGN(total_valid_blks_available,
+			NAT_ENTRY_PER_BLOCK);
 
 	set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
 	/*
@@ -255,16 +258,20 @@
 
 	/*
 	 * It should be reserved minimum 1 segment for nat.
-	 * When sit is too large, we should expand cp area. It requires more pages for cp.
+	 * When sit is too large, we should expand cp area. It requires more
+	 * pages for cp.
 	 */
 	if (max_sit_bitmap_size >
-			(CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) {
-		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1;
+			(CHECKSUM_OFFSET -
+				sizeof(struct f2fs_checkpoint) + 1 - 64)) {
+		max_nat_bitmap_size = CHECKSUM_OFFSET -
+				sizeof(struct f2fs_checkpoint) + 1;
 		set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size));
 	} else {
-		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
+		max_nat_bitmap_size =
+			CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
 			- max_sit_bitmap_size;
-		sb.cp_payload = 0;
+		set_sb(cp_payload, 0);
 	}
 
 	max_nat_segments = (max_nat_bitmap_size * 8) >> log_blks_per_seg;
@@ -275,16 +282,16 @@
 	set_sb(segment_count_nat, get_sb(segment_count_nat) * 2);
 
 	set_sb(ssa_blkaddr, get_sb(nat_blkaddr) + get_sb(segment_count_nat) *
-			config.blks_per_seg);
+			c.blks_per_seg);
 
 	total_valid_blks_available = (get_sb(segment_count) -
 			(get_sb(segment_count_ckpt) +
 			get_sb(segment_count_sit) +
 			get_sb(segment_count_nat))) *
-			config.blks_per_seg;
+			c.blks_per_seg;
 
 	blocks_for_ssa = total_valid_blks_available /
-				config.blks_per_seg + 1;
+				c.blks_per_seg + 1;
 
 	set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
 
@@ -292,77 +299,102 @@
 		get_sb(segment_count_sit) +
 		get_sb(segment_count_nat) +
 		get_sb(segment_count_ssa);
-	diff = total_meta_segments % (config.segs_per_zone);
+	diff = total_meta_segments % (c.segs_per_zone);
 	if (diff)
 		set_sb(segment_count_ssa, get_sb(segment_count_ssa) +
-			(config.segs_per_zone - diff));
+			(c.segs_per_zone - diff));
 
-	set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) *
-			 config.blks_per_seg);
+	total_meta_zones = ZONE_ALIGN(total_meta_segments *
+						c.blks_per_seg);
 
-	set_sb(segment_count_main, get_sb(segment_count) -
-			(get_sb(segment_count_ckpt) +
-			 get_sb(segment_count_sit) +
-			 get_sb(segment_count_nat) +
-			 get_sb(segment_count_ssa)));
+	set_sb(main_blkaddr, get_sb(segment0_blkaddr) + total_meta_zones *
+				c.segs_per_zone * c.blks_per_seg);
 
-	set_sb(section_count, get_sb(segment_count_main) / config.segs_per_sec);
+	if (c.zoned_mode) {
+		/*
+		 * Make sure there is enough randomly writeable
+		 * space at the beginning of the disk.
+		 */
+		unsigned long main_blkzone = get_sb(main_blkaddr) / c.zone_blocks;
 
-	set_sb(segment_count_main, get_sb(section_count) * config.segs_per_sec);
+		if (c.devices[0].zoned_model == F2FS_ZONED_HM &&
+				c.devices[0].nr_rnd_zones < main_blkzone) {
+			MSG(0, "\tError: Device does not have enough random "
+					"write zones for F2FS volume (%lu needed)\n",
+					main_blkzone);
+			return -1;
+		}
+	}
 
-	if ((get_sb(segment_count_main) - 2) <
-					config.reserved_segments) {
-		MSG(1, "\tError: Device size is not sufficient for F2FS volume,\
-			more segment needed =%u",
-			config.reserved_segments -
-			(get_sb(segment_count_main) - 2));
+	total_zones = get_sb(segment_count) / (c.segs_per_zone) -
+							total_meta_zones;
+
+	set_sb(section_count, total_zones * c.secs_per_zone);
+
+	set_sb(segment_count_main, get_sb(section_count) * c.segs_per_sec);
+
+	/* Let's determine the best reserved and overprovisioned space */
+	if (c.overprovision == 0)
+		c.overprovision = get_best_overprovision(sb);
+
+	c.reserved_segments =
+			(2 * (100 / c.overprovision + 1) + 6)
+			* c.segs_per_sec;
+
+	if (c.overprovision == 0 || c.total_segments < F2FS_MIN_SEGMENTS ||
+		(c.devices[0].total_sectors *
+			c.sector_size < zone_align_start_offset) ||
+		(get_sb(segment_count_main) - 2) < c.reserved_segments) {
+		MSG(0, "\tError: Device size is not sufficient for F2FS volume\n");
 		return -1;
 	}
 
-	uuid_generate(sb.uuid);
+	uuid_generate(sb->uuid);
 
-	ASCIIToUNICODE(sb.volume_name, (u_int8_t *)config.vol_label);
-
+	utf8_to_utf16(sb->volume_name, (const char *)c.vol_label,
+				MAX_VOLUME_NAME, strlen(c.vol_label));
 	set_sb(node_ino, 1);
 	set_sb(meta_ino, 2);
 	set_sb(root_ino, 3);
 
-	total_zones = get_sb(segment_count_main) / (config.segs_per_zone);
 	if (total_zones <= 6) {
-		MSG(1, "\tError: %d zones: Need more zones \
-			by shrinking zone size\n", total_zones);
+		MSG(1, "\tError: %d zones: Need more zones "
+			"by shrinking zone size\n", total_zones);
 		return -1;
 	}
 
-	if (config.heap) {
-		config.cur_seg[CURSEG_HOT_NODE] = last_section(last_zone(total_zones));
-		config.cur_seg[CURSEG_WARM_NODE] = prev_zone(CURSEG_HOT_NODE);
-		config.cur_seg[CURSEG_COLD_NODE] = prev_zone(CURSEG_WARM_NODE);
-		config.cur_seg[CURSEG_HOT_DATA] = prev_zone(CURSEG_COLD_NODE);
-		config.cur_seg[CURSEG_COLD_DATA] = 0;
-		config.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
+	if (c.heap) {
+		c.cur_seg[CURSEG_HOT_NODE] =
+				last_section(last_zone(total_zones));
+		c.cur_seg[CURSEG_WARM_NODE] = prev_zone(CURSEG_HOT_NODE);
+		c.cur_seg[CURSEG_COLD_NODE] = prev_zone(CURSEG_WARM_NODE);
+		c.cur_seg[CURSEG_HOT_DATA] = prev_zone(CURSEG_COLD_NODE);
+		c.cur_seg[CURSEG_COLD_DATA] = 0;
+		c.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
 	} else {
-		config.cur_seg[CURSEG_HOT_NODE] = 0;
-		config.cur_seg[CURSEG_WARM_NODE] = next_zone(CURSEG_HOT_NODE);
-		config.cur_seg[CURSEG_COLD_NODE] = next_zone(CURSEG_WARM_NODE);
-		config.cur_seg[CURSEG_HOT_DATA] = next_zone(CURSEG_COLD_NODE);
-		config.cur_seg[CURSEG_COLD_DATA] = next_zone(CURSEG_HOT_DATA);
-		config.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
+		c.cur_seg[CURSEG_HOT_NODE] = 0;
+		c.cur_seg[CURSEG_WARM_NODE] = next_zone(CURSEG_HOT_NODE);
+		c.cur_seg[CURSEG_COLD_NODE] = next_zone(CURSEG_WARM_NODE);
+		c.cur_seg[CURSEG_HOT_DATA] = next_zone(CURSEG_COLD_NODE);
+		c.cur_seg[CURSEG_COLD_DATA] = next_zone(CURSEG_HOT_DATA);
+		c.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
 	}
 
-	configure_extension_list();
+	cure_extension_list();
 
 	/* get kernel version */
-	if (config.kd >= 0) {
-		dev_read_version(config.version, 0, VERSION_LEN);
-		get_kernel_version(config.version);
-		MSG(0, "Info: format version with\n  \"%s\"\n", config.version);
+	if (c.kd >= 0) {
+		dev_read_version(c.version, 0, VERSION_LEN);
+		get_kernel_version(c.version);
+		MSG(0, "Info: format version with\n  \"%s\"\n", c.version);
 	} else {
-		memset(config.version, 0, VERSION_LEN);
+		memset(c.version, 0, VERSION_LEN);
 	}
 
-	memcpy(sb.version, config.version, VERSION_LEN);
-	memcpy(sb.init_version, config.version, VERSION_LEN);
+	memcpy(sb->version, c.version, VERSION_LEN);
+	memcpy(sb->init_version, c.version, VERSION_LEN);
+
+	sb->feature = c.feature;
 
 	return 0;
 }
@@ -389,8 +421,8 @@
 	DBG(1, "\tFilling sit area at offset 0x%08"PRIx64"\n", sit_seg_addr);
 	for (index = 0; index < (get_sb(segment_count_sit) / 2); index++) {
 		if (dev_fill(zero_buf, sit_seg_addr, seg_size)) {
-			MSG(1, "\tError: While zeroing out the sit area \
-					on disk!!!\n");
+			MSG(1, "\tError: While zeroing out the sit area "
+					"on disk!!!\n");
 			free(zero_buf);
 			return -1;
 		}
@@ -423,8 +455,8 @@
 	DBG(1, "\tFilling nat area at offset 0x%08"PRIx64"\n", nat_seg_addr);
 	for (index = 0; index < get_sb(segment_count_nat) / 2; index++) {
 		if (dev_fill(nat_buf, nat_seg_addr, seg_size)) {
-			MSG(1, "\tError: While zeroing out the nat area \
-					on disk!!!\n");
+			MSG(1, "\tError: While zeroing out the nat area "
+					"on disk!!!\n");
 			free(nat_buf);
 			return -1;
 		}
@@ -438,8 +470,9 @@
 static int f2fs_write_check_point_pack(void)
 {
 	struct f2fs_summary_block *sum = NULL;
+	struct f2fs_journal *journal;
 	u_int32_t blk_size_bytes;
-	u_int64_t cp_seg_blk_offset = 0;
+	u_int64_t cp_seg_blk = 0;
 	u_int32_t crc = 0;
 	unsigned int i;
 	char *cp_payload = NULL;
@@ -460,7 +493,7 @@
 	}
 
 	sum_compact = calloc(F2FS_BLKSIZE, 1);
-	if (sum == NULL) {
+	if (sum_compact == NULL) {
 		MSG(1, "\tError: Calloc Failed for summay buffer!!!\n");
 		goto free_sum;
 	}
@@ -473,13 +506,13 @@
 	}
 
 	/* 1. cp page 1 of checkpoint pack 1 */
-	set_cp(checkpoint_ver, 1);
-	set_cp(cur_node_segno[0], config.cur_seg[CURSEG_HOT_NODE]);
-	set_cp(cur_node_segno[1], config.cur_seg[CURSEG_WARM_NODE]);
-	set_cp(cur_node_segno[2], config.cur_seg[CURSEG_COLD_NODE]);
-	set_cp(cur_data_segno[0], config.cur_seg[CURSEG_HOT_DATA]);
-	set_cp(cur_data_segno[1], config.cur_seg[CURSEG_WARM_DATA]);
-	set_cp(cur_data_segno[2], config.cur_seg[CURSEG_COLD_DATA]);
+	cp->checkpoint_ver = rand() | 0x1;
+	set_cp(cur_node_segno[0], c.cur_seg[CURSEG_HOT_NODE]);
+	set_cp(cur_node_segno[1], c.cur_seg[CURSEG_WARM_NODE]);
+	set_cp(cur_node_segno[2], c.cur_seg[CURSEG_COLD_NODE]);
+	set_cp(cur_data_segno[0], c.cur_seg[CURSEG_HOT_DATA]);
+	set_cp(cur_data_segno[1], c.cur_seg[CURSEG_WARM_DATA]);
+	set_cp(cur_data_segno[2], c.cur_seg[CURSEG_COLD_DATA]);
 	for (i = 3; i < MAX_ACTIVE_NODE_LOGS; i++) {
 		set_cp(cur_node_segno[i], 0xffffffff);
 		set_cp(cur_data_segno[i], 0xffffffff);
@@ -488,17 +521,22 @@
 	set_cp(cur_node_blkoff[0], 1);
 	set_cp(cur_data_blkoff[0], 1);
 	set_cp(valid_block_count, 2);
-	set_cp(rsvd_segment_count, config.reserved_segments);
+	set_cp(rsvd_segment_count, c.reserved_segments);
 	set_cp(overprov_segment_count, (get_sb(segment_count_main) -
 			get_cp(rsvd_segment_count)) *
-			config.overprovision / 100);
+			c.overprovision / 100);
 	set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
 			get_cp(rsvd_segment_count));
 
+	MSG(0, "Info: Overprovision ratio = %.3lf%%\n", c.overprovision);
+	MSG(0, "Info: Overprovision segments = %u (GC reserved = %u)\n",
+					get_cp(overprov_segment_count),
+					c.reserved_segments);
+
 	/* main segments - reserved segments - (node + data segments) */
 	set_cp(free_segment_count, get_sb(segment_count_main) - 6);
 	set_cp(user_block_count, ((get_cp(free_segment_count) + 6 -
-			get_cp(overprov_segment_count)) * config.blks_per_seg));
+			get_cp(overprov_segment_count)) * c.blks_per_seg));
 	/* cp page (2), data summaries (1), node summaries (3) */
 	set_cp(cp_pack_total_block_count, 6 + get_sb(cp_payload));
 	set_cp(ckpt_flags, CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG);
@@ -519,20 +557,27 @@
 							cpu_to_le32(crc);
 
 	blk_size_bytes = 1 << get_sb(log_blocksize);
-	cp_seg_blk_offset = get_sb(segment0_blkaddr);
-	cp_seg_blk_offset *= blk_size_bytes;
 
-	DBG(1, "\tWriting main segments, cp at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
-	if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+	if (blk_size_bytes != F2FS_BLKSIZE) {
+		MSG(1, "\tError: Wrong block size %d / %d!!!\n",
+					blk_size_bytes, F2FS_BLKSIZE);
+		goto free_cp_payload;
+	}
+
+	cp_seg_blk = get_sb(segment0_blkaddr);
+
+	DBG(1, "\tWriting main segments, cp at offset 0x%08"PRIx64"\n",
+						cp_seg_blk);
+	if (dev_write_block(cp, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the cp to disk!!!\n");
 		goto free_cp_payload;
 	}
 
 	for (i = 0; i < get_sb(cp_payload); i++) {
-		cp_seg_blk_offset += blk_size_bytes;
-		if (dev_fill(cp_payload, cp_seg_blk_offset, blk_size_bytes)) {
-			MSG(1, "\tError: While zeroing out the sit bitmap area \
-					on disk!!!\n");
+		cp_seg_blk++;
+		if (dev_fill_block(cp_payload, cp_seg_blk)) {
+			MSG(1, "\tError: While zeroing out the sit bitmap area "
+					"on disk!!!\n");
 			goto free_cp_payload;
 		}
 	}
@@ -555,51 +600,58 @@
 	memset(sum, 0, sizeof(struct f2fs_summary_block));
 	SET_SUM_TYPE((&sum->footer), SUM_TYPE_DATA);
 
-	sum->n_nats = cpu_to_le16(1);
-	sum->nat_j.entries[0].nid = sb.root_ino;
-	sum->nat_j.entries[0].ne.version = 0;
-	sum->nat_j.entries[0].ne.ino = sb.root_ino;
-	sum->nat_j.entries[0].ne.block_addr = cpu_to_le32(
+	journal = &sum->journal;
+	journal->n_nats = cpu_to_le16(1);
+	journal->nat_j.entries[0].nid = sb->root_ino;
+	journal->nat_j.entries[0].ne.version = 0;
+	journal->nat_j.entries[0].ne.ino = sb->root_ino;
+	journal->nat_j.entries[0].ne.block_addr = cpu_to_le32(
 			get_sb(main_blkaddr) +
-			get_cp(cur_node_segno[0]) * config.blks_per_seg);
+			get_cp(cur_node_segno[0]) * c.blks_per_seg);
 
-	memcpy(sum_compact_p, &sum->n_nats, SUM_JOURNAL_SIZE);
+	memcpy(sum_compact_p, &journal->n_nats, SUM_JOURNAL_SIZE);
 	sum_compact_p += SUM_JOURNAL_SIZE;
 
 	memset(sum, 0, sizeof(struct f2fs_summary_block));
 	/* inode sit for root */
-	sum->n_sits = cpu_to_le16(6);
-	sum->sit_j.entries[0].segno = cp->cur_node_segno[0];
-	sum->sit_j.entries[0].se.vblocks = cpu_to_le16((CURSEG_HOT_NODE << 10) | 1);
-	f2fs_set_bit(0, (char *)sum->sit_j.entries[0].se.valid_map);
-	sum->sit_j.entries[1].segno = cp->cur_node_segno[1];
-	sum->sit_j.entries[1].se.vblocks = cpu_to_le16((CURSEG_WARM_NODE << 10));
-	sum->sit_j.entries[2].segno = cp->cur_node_segno[2];
-	sum->sit_j.entries[2].se.vblocks = cpu_to_le16((CURSEG_COLD_NODE << 10));
+	journal->n_sits = cpu_to_le16(6);
+	journal->sit_j.entries[0].segno = cp->cur_node_segno[0];
+	journal->sit_j.entries[0].se.vblocks =
+				cpu_to_le16((CURSEG_HOT_NODE << 10) | 1);
+	f2fs_set_bit(0, (char *)journal->sit_j.entries[0].se.valid_map);
+	journal->sit_j.entries[1].segno = cp->cur_node_segno[1];
+	journal->sit_j.entries[1].se.vblocks =
+				cpu_to_le16((CURSEG_WARM_NODE << 10));
+	journal->sit_j.entries[2].segno = cp->cur_node_segno[2];
+	journal->sit_j.entries[2].se.vblocks =
+				cpu_to_le16((CURSEG_COLD_NODE << 10));
 
 	/* data sit for root */
-	sum->sit_j.entries[3].segno = cp->cur_data_segno[0];
-	sum->sit_j.entries[3].se.vblocks = cpu_to_le16((CURSEG_HOT_DATA << 10) | 1);
-	f2fs_set_bit(0, (char *)sum->sit_j.entries[3].se.valid_map);
-	sum->sit_j.entries[4].segno = cp->cur_data_segno[1];
-	sum->sit_j.entries[4].se.vblocks = cpu_to_le16((CURSEG_WARM_DATA << 10));
-	sum->sit_j.entries[5].segno = cp->cur_data_segno[2];
-	sum->sit_j.entries[5].se.vblocks = cpu_to_le16((CURSEG_COLD_DATA << 10));
+	journal->sit_j.entries[3].segno = cp->cur_data_segno[0];
+	journal->sit_j.entries[3].se.vblocks =
+				cpu_to_le16((CURSEG_HOT_DATA << 10) | 1);
+	f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map);
+	journal->sit_j.entries[4].segno = cp->cur_data_segno[1];
+	journal->sit_j.entries[4].se.vblocks =
+				cpu_to_le16((CURSEG_WARM_DATA << 10));
+	journal->sit_j.entries[5].segno = cp->cur_data_segno[2];
+	journal->sit_j.entries[5].se.vblocks =
+				cpu_to_le16((CURSEG_COLD_DATA << 10));
 
-	memcpy(sum_compact_p, &sum->n_sits, SUM_JOURNAL_SIZE);
+	memcpy(sum_compact_p, &journal->n_sits, SUM_JOURNAL_SIZE);
 	sum_compact_p += SUM_JOURNAL_SIZE;
 
 	/* hot data summary */
 	sum_entry = (struct f2fs_summary *)sum_compact_p;
-	sum_entry->nid = sb.root_ino;
+	sum_entry->nid = sb->root_ino;
 	sum_entry->ofs_in_node = 0;
 	/* warm data summary, nothing to do */
 	/* cold data summary, nothing to do */
 
-	cp_seg_blk_offset += blk_size_bytes;
+	cp_seg_blk++;
 	DBG(1, "\tWriting Segment summary for HOT/WARM/COLD_DATA, at offset 0x%08"PRIx64"\n",
-			cp_seg_blk_offset);
-	if (dev_write(sum_compact, cp_seg_blk_offset, blk_size_bytes)) {
+			cp_seg_blk);
+	if (dev_write_block(sum_compact, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
 		goto free_cp_payload;
 	}
@@ -608,13 +660,13 @@
 	memset(sum, 0, sizeof(struct f2fs_summary_block));
 	SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE);
 
-	sum->entries[0].nid = sb.root_ino;
+	sum->entries[0].nid = sb->root_ino;
 	sum->entries[0].ofs_in_node = 0;
 
-	cp_seg_blk_offset += blk_size_bytes;
+	cp_seg_blk++;
 	DBG(1, "\tWriting Segment summary for HOT_NODE, at offset 0x%08"PRIx64"\n",
-			cp_seg_blk_offset);
-	if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) {
+			cp_seg_blk);
+	if (dev_write_block(sum, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
 		goto free_cp_payload;
 	}
@@ -623,10 +675,10 @@
 	memset(sum, 0, sizeof(struct f2fs_summary_block));
 	SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE);
 
-	cp_seg_blk_offset += blk_size_bytes;
+	cp_seg_blk++;
 	DBG(1, "\tWriting Segment summary for WARM_NODE, at offset 0x%08"PRIx64"\n",
-			cp_seg_blk_offset);
-	if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) {
+			cp_seg_blk);
+	if (dev_write_block(sum, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
 		goto free_cp_payload;
 	}
@@ -634,18 +686,18 @@
 	/* Fill segment summary for COLD_NODE to zero. */
 	memset(sum, 0, sizeof(struct f2fs_summary_block));
 	SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE);
-	cp_seg_blk_offset += blk_size_bytes;
+	cp_seg_blk++;
 	DBG(1, "\tWriting Segment summary for COLD_NODE, at offset 0x%08"PRIx64"\n",
-			cp_seg_blk_offset);
-	if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) {
+			cp_seg_blk);
+	if (dev_write_block(sum, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
 		goto free_cp_payload;
 	}
 
 	/* cp page2 */
-	cp_seg_blk_offset += blk_size_bytes;
-	DBG(1, "\tWriting cp page2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
-	if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+	cp_seg_blk++;
+	DBG(1, "\tWriting cp page2, at offset 0x%08"PRIx64"\n", cp_seg_blk);
+	if (dev_write_block(cp, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the cp to disk!!!\n");
 		goto free_cp_payload;
 	}
@@ -658,29 +710,29 @@
 	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
 	*((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) =
 							cpu_to_le32(crc);
-	cp_seg_blk_offset = (get_sb(segment0_blkaddr) +
-				config.blks_per_seg) *
-				blk_size_bytes;
-	DBG(1, "\tWriting cp page 1 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
-	if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+	cp_seg_blk = get_sb(segment0_blkaddr) + c.blks_per_seg;
+	DBG(1, "\tWriting cp page 1 of checkpoint pack 2, at offset 0x%08"PRIx64"\n",
+				cp_seg_blk);
+	if (dev_write_block(cp, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the cp to disk!!!\n");
 		goto free_cp_payload;
 	}
 
 	for (i = 0; i < get_sb(cp_payload); i++) {
-		cp_seg_blk_offset += blk_size_bytes;
-		if (dev_fill(cp_payload, cp_seg_blk_offset, blk_size_bytes)) {
-			MSG(1, "\tError: While zeroing out the sit bitmap area \
-					on disk!!!\n");
+		cp_seg_blk++;
+		if (dev_fill_block(cp_payload, cp_seg_blk)) {
+			MSG(1, "\tError: While zeroing out the sit bitmap area "
+					"on disk!!!\n");
 			goto free_cp_payload;
 		}
 	}
 
 	/* cp page 2 of check point pack 2 */
-	cp_seg_blk_offset += blk_size_bytes * (le32_to_cpu(cp->cp_pack_total_block_count)
-			- get_sb(cp_payload) - 1);
-	DBG(1, "\tWriting cp page 2 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
-	if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+	cp_seg_blk += (le32_to_cpu(cp->cp_pack_total_block_count) -
+					get_sb(cp_payload) - 1);
+	DBG(1, "\tWriting cp page 2 of checkpoint pack 2, at offset 0x%08"PRIx64"\n",
+				cp_seg_blk);
+	if (dev_write_block(cp, cp_seg_blk)) {
 		MSG(1, "\tError: While writing the cp to disk!!!\n");
 		goto free_cp_payload;
 	}
@@ -705,13 +757,12 @@
 
 	zero_buff = calloc(F2FS_BLKSIZE, 1);
 
-	memcpy(zero_buff + F2FS_SUPER_OFFSET, &sb,
-						sizeof(sb));
+	memcpy(zero_buff + F2FS_SUPER_OFFSET, sb, sizeof(*sb));
 	DBG(1, "\tWriting super block, at offset 0x%08x\n", 0);
 	for (index = 0; index < 2; index++) {
-		if (dev_write(zero_buff, index * F2FS_BLKSIZE, F2FS_BLKSIZE)) {
-			MSG(1, "\tError: While while writing supe_blk \
-					on disk!!! index : %d\n", index);
+		if (dev_write_block(zero_buff, index)) {
+			MSG(1, "\tError: While while writing supe_blk "
+					"on disk!!! index : %d\n", index);
 			free(zero_buff);
 			return -1;
 		}
@@ -721,6 +772,35 @@
 	return 0;
 }
 
+#ifndef WITH_ANDROID
+static int discard_obsolete_dnode(struct f2fs_node *raw_node, u_int64_t offset)
+{
+	if (c.zoned_mode)
+		return 0;
+	do {
+		if (offset < get_sb(main_blkaddr) ||
+			offset >= get_sb(main_blkaddr) + get_sb(block_count))
+			break;
+
+		if (dev_read_block(raw_node, offset)) {
+			MSG(1, "\tError: While traversing direct node!!!\n");
+			return -1;
+		}
+
+		memset(raw_node, 0, F2FS_BLKSIZE);
+
+		DBG(1, "\tDiscard dnode, at offset 0x%08"PRIx64"\n", offset);
+		if (dev_write_block(raw_node, offset)) {
+			MSG(1, "\tError: While discarding direct node!!!\n");
+			return -1;
+		}
+		offset = le32_to_cpu(raw_node->footer.next_blkaddr);
+	} while (1);
+
+	return 0;
+}
+#endif
+
 static int f2fs_write_root_inode(void)
 {
 	struct f2fs_node *raw_node = NULL;
@@ -733,13 +813,13 @@
 		return -1;
 	}
 
-	raw_node->footer.nid = sb.root_ino;
-	raw_node->footer.ino = sb.root_ino;
+	raw_node->footer.nid = sb->root_ino;
+	raw_node->footer.ino = sb->root_ino;
 	raw_node->footer.cp_ver = cpu_to_le64(1);
 	raw_node->footer.next_blkaddr = cpu_to_le32(
 			get_sb(main_blkaddr) +
-			config.cur_seg[CURSEG_HOT_NODE] *
-			config.blks_per_seg + 1);
+			c.cur_seg[CURSEG_HOT_NODE] *
+			c.blks_per_seg + 1);
 
 	raw_node->i.i_mode = cpu_to_le16(0x41ed);
 	raw_node->i.i_links = cpu_to_le32(2);
@@ -763,39 +843,39 @@
 	raw_node->i.i_dir_level = DEF_DIR_LEVEL;
 
 	data_blk_nor = get_sb(main_blkaddr) +
-		config.cur_seg[CURSEG_HOT_DATA] * config.blks_per_seg;
+		c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg;
 	raw_node->i.i_addr[0] = cpu_to_le32(data_blk_nor);
 
 	raw_node->i.i_ext.fofs = 0;
-	raw_node->i.i_ext.blk_addr = cpu_to_le32(data_blk_nor);
-	raw_node->i.i_ext.len = cpu_to_le32(1);
+	raw_node->i.i_ext.blk_addr = 0;
+	raw_node->i.i_ext.len = 0;
 
 	main_area_node_seg_blk_offset = get_sb(main_blkaddr);
-	main_area_node_seg_blk_offset += config.cur_seg[CURSEG_HOT_NODE] *
-					config.blks_per_seg;
-        main_area_node_seg_blk_offset *= blk_size_bytes;
+	main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] *
+					c.blks_per_seg;
 
-	DBG(1, "\tWriting root inode (hot node), at offset 0x%08"PRIx64"\n", main_area_node_seg_blk_offset);
-	if (dev_write(raw_node, main_area_node_seg_blk_offset, F2FS_BLKSIZE)) {
+	DBG(1, "\tWriting root inode (hot node), %x %x %x at offset 0x%08"PRIu64"\n",
+			get_sb(main_blkaddr),
+			c.cur_seg[CURSEG_HOT_NODE],
+			c.blks_per_seg, main_area_node_seg_blk_offset);
+	if (dev_write_block(raw_node, main_area_node_seg_blk_offset)) {
 		MSG(1, "\tError: While writing the raw_node to disk!!!\n");
 		free(raw_node);
 		return -1;
 	}
 
-	memset(raw_node, 0xff, sizeof(struct f2fs_node));
-
 	/* avoid power-off-recovery based on roll-forward policy */
 	main_area_node_seg_blk_offset = get_sb(main_blkaddr);
-	main_area_node_seg_blk_offset += config.cur_seg[CURSEG_WARM_NODE] *
-					config.blks_per_seg;
-        main_area_node_seg_blk_offset *= blk_size_bytes;
+	main_area_node_seg_blk_offset += c.cur_seg[CURSEG_WARM_NODE] *
+					c.blks_per_seg;
 
-	DBG(1, "\tWriting root inode (warm node), at offset 0x%08"PRIx64"\n", main_area_node_seg_blk_offset);
-	if (dev_write(raw_node, main_area_node_seg_blk_offset, F2FS_BLKSIZE)) {
-		MSG(1, "\tError: While writing the raw_node to disk!!!\n");
+#ifndef WITH_ANDROID
+	if (discard_obsolete_dnode(raw_node, main_area_node_seg_blk_offset)) {
 		free(raw_node);
 		return -1;
 	}
+#endif
+
 	free(raw_node);
 	return 0;
 }
@@ -803,7 +883,7 @@
 static int f2fs_update_nat_root(void)
 {
 	struct f2fs_nat_block *nat_blk = NULL;
-	u_int64_t blk_size_bytes, nat_seg_blk_offset = 0;
+	u_int64_t nat_seg_blk_offset = 0;
 
 	nat_blk = calloc(F2FS_BLKSIZE, 1);
 	if(nat_blk == NULL) {
@@ -814,23 +894,22 @@
 	/* update root */
 	nat_blk->entries[get_sb(root_ino)].block_addr = cpu_to_le32(
 		get_sb(main_blkaddr) +
-		config.cur_seg[CURSEG_HOT_NODE] * config.blks_per_seg);
-	nat_blk->entries[get_sb(root_ino)].ino = sb.root_ino;
+		c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg);
+	nat_blk->entries[get_sb(root_ino)].ino = sb->root_ino;
 
 	/* update node nat */
 	nat_blk->entries[get_sb(node_ino)].block_addr = cpu_to_le32(1);
-	nat_blk->entries[get_sb(node_ino)].ino = sb.node_ino;
+	nat_blk->entries[get_sb(node_ino)].ino = sb->node_ino;
 
 	/* update meta nat */
 	nat_blk->entries[get_sb(meta_ino)].block_addr = cpu_to_le32(1);
-	nat_blk->entries[get_sb(meta_ino)].ino = sb.meta_ino;
+	nat_blk->entries[get_sb(meta_ino)].ino = sb->meta_ino;
 
-	blk_size_bytes = 1 << get_sb(log_blocksize);
 	nat_seg_blk_offset = get_sb(nat_blkaddr);
-	nat_seg_blk_offset *= blk_size_bytes;
 
-	DBG(1, "\tWriting nat root, at offset 0x%08"PRIx64"\n", nat_seg_blk_offset);
-	if (dev_write(nat_blk, nat_seg_blk_offset, F2FS_BLKSIZE)) {
+	DBG(1, "\tWriting nat root, at offset 0x%08"PRIx64"\n",
+					nat_seg_blk_offset);
+	if (dev_write_block(nat_blk, nat_seg_blk_offset)) {
 		MSG(1, "\tError: While writing the nat_blk set0 to disk!\n");
 		free(nat_blk);
 		return -1;
@@ -843,7 +922,7 @@
 static int f2fs_add_default_dentry_root(void)
 {
 	struct f2fs_dentry_block *dent_blk = NULL;
-	u_int64_t blk_size_bytes, data_blk_offset = 0;
+	u_int64_t data_blk_offset = 0;
 
 	dent_blk = calloc(F2FS_BLKSIZE, 1);
 	if(dent_blk == NULL) {
@@ -852,27 +931,27 @@
 	}
 
 	dent_blk->dentry[0].hash_code = 0;
-	dent_blk->dentry[0].ino = sb.root_ino;
+	dent_blk->dentry[0].ino = sb->root_ino;
 	dent_blk->dentry[0].name_len = cpu_to_le16(1);
 	dent_blk->dentry[0].file_type = F2FS_FT_DIR;
 	memcpy(dent_blk->filename[0], ".", 1);
 
 	dent_blk->dentry[1].hash_code = 0;
-	dent_blk->dentry[1].ino = sb.root_ino;
+	dent_blk->dentry[1].ino = sb->root_ino;
 	dent_blk->dentry[1].name_len = cpu_to_le16(2);
 	dent_blk->dentry[1].file_type = F2FS_FT_DIR;
 	memcpy(dent_blk->filename[1], "..", 2);
 
 	/* bitmap for . and .. */
-	dent_blk->dentry_bitmap[0] = (1 << 1) | (1 << 0);
-	blk_size_bytes = 1 << get_sb(log_blocksize);
+	test_and_set_bit_le(0, dent_blk->dentry_bitmap);
+	test_and_set_bit_le(1, dent_blk->dentry_bitmap);
 	data_blk_offset = get_sb(main_blkaddr);
-	data_blk_offset += config.cur_seg[CURSEG_HOT_DATA] *
-				config.blks_per_seg;
-	data_blk_offset *= blk_size_bytes;
+	data_blk_offset += c.cur_seg[CURSEG_HOT_DATA] *
+				c.blks_per_seg;
 
-	DBG(1, "\tWriting default dentry root, at offset 0x%08"PRIx64"\n", data_blk_offset);
-	if (dev_write(dent_blk, data_blk_offset, F2FS_BLKSIZE)) {
+	DBG(1, "\tWriting default dentry root, at offset 0x%08"PRIx64"\n",
+				data_blk_offset);
+	if (dev_write_block(dent_blk, data_blk_offset)) {
 		MSG(1, "\tError: While writing the dentry_blk to disk!!!\n");
 		free(dent_blk);
 		return -1;
@@ -920,10 +999,12 @@
 		goto exit;
 	}
 
-	err = f2fs_trim_device();
-	if (err < 0) {
-		MSG(0, "\tError: Failed to trim whole device!!!\n");
-		goto exit;
+	if (c.trim) {
+		err = f2fs_trim_devices();
+		if (err < 0) {
+			MSG(0, "\tError: Failed to trim whole device!!!\n");
+			goto exit;
+		}
 	}
 
 	err = f2fs_init_sit_area();
diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c
index bba2085..5bb1faf 100644
--- a/mkfs/f2fs_format_main.c
+++ b/mkfs/f2fs_format_main.c
@@ -16,76 +16,117 @@
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <time.h>
-//#include <linux/fs.h>
 #include <uuid/uuid.h>
 
 #include "f2fs_fs.h"
 #include "f2fs_format_utils.h"
 
-extern struct f2fs_configuration config;
+extern struct f2fs_configuration c;
 
 static void mkfs_usage()
 {
 	MSG(0, "\nUsage: mkfs.f2fs [options] device [sectors]\n");
 	MSG(0, "[options]:\n");
 	MSG(0, "  -a heap-based allocation [default:1]\n");
+	MSG(0, "  -c [device path] up to 7 devices excepts meta device\n");
 	MSG(0, "  -d debug level [default:0]\n");
 	MSG(0, "  -e [extension list] e.g. \"mp3,gif,mov\"\n");
 	MSG(0, "  -l label\n");
 	MSG(0, "  -o overprovision ratio [default:5]\n");
+	MSG(0, "  -O set feature\n");
+	MSG(0, "  -q quiet mode\n");
 	MSG(0, "  -s # of segments per section [default:1]\n");
 	MSG(0, "  -z # of sections per zone [default:1]\n");
 	MSG(0, "  -t 0: nodiscard, 1: discard [default:1]\n");
+	MSG(0, "  -m support zoned block device [default:0]\n");
 	MSG(0, "sectors: number of sectors. [default: determined by device size]\n");
 	exit(1);
 }
 
+static void f2fs_show_info()
+{
+	MSG(0, "\n\tF2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n",
+				F2FS_TOOLS_VERSION,
+				F2FS_TOOLS_DATE);
+	if (c.heap == 0)
+		MSG(0, "Info: Disable heap-based policy\n");
+
+	MSG(0, "Info: Debug level = %d\n", c.dbg_lv);
+	if (c.extension_list)
+		MSG(0, "Info: Add new extension list\n");
+
+	if (c.vol_label)
+		MSG(0, "Info: Label = %s\n", c.vol_label);
+	MSG(0, "Info: Trim is %s\n", c.trim ? "enabled": "disabled");
+}
+
+static void parse_feature(const char *features)
+{
+	if (!strcmp(features, "encrypt")) {
+		c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
+	} else {
+		MSG(0, "Error: Wrong features\n");
+		mkfs_usage();
+	}
+}
+
 static void f2fs_parse_options(int argc, char *argv[])
 {
-	static const char *option_string = "a:d:e:l:o:s:z:t:";
+	static const char *option_string = "qa:c:d:e:l:mo:O:s:z:t:";
 	int32_t option=0;
 
 	while ((option = getopt(argc,argv,option_string)) != EOF) {
 		switch (option) {
+		case 'q':
+			c.dbg_lv = -1;
+			break;
 		case 'a':
-			config.heap = atoi(optarg);
-			if (config.heap == 0)
-				MSG(0, "Info: Disable heap-based policy\n");
+			c.heap = atoi(optarg);
+			break;
+		case 'c':
+			if (c.ndevs >= MAX_DEVICES) {
+				MSG(0, "Error: Too many devices\n");
+				mkfs_usage();
+			}
+
+			if (strlen(optarg) > MAX_PATH_LEN) {
+				MSG(0, "Error: device path should be less than "
+					"%d characters\n", MAX_PATH_LEN);
+				mkfs_usage();
+			}
+			c.devices[c.ndevs++].path = strdup(optarg);
 			break;
 		case 'd':
-			config.dbg_lv = atoi(optarg);
-			MSG(0, "Info: Debug level = %d\n", config.dbg_lv);
+			c.dbg_lv = atoi(optarg);
 			break;
 		case 'e':
-			config.extension_list = strdup(optarg);
-			MSG(0, "Info: Add new extension list\n");
+			c.extension_list = strdup(optarg);
 			break;
 		case 'l':		/*v: volume label */
 			if (strlen(optarg) > 512) {
-				MSG(0, "Error: Volume Label should be less than\
-						512 characters\n");
+				MSG(0, "Error: Volume Label should be less than "
+						"512 characters\n");
 				mkfs_usage();
 			}
-			config.vol_label = optarg;
-			MSG(0, "Info: Label = %s\n", config.vol_label);
+			c.vol_label = optarg;
+			break;
+		case 'm':
+			c.zoned_mode = 1;
 			break;
 		case 'o':
-			config.overprovision = atoi(optarg);
-			MSG(0, "Info: Overprovision ratio = %u%%\n",
-								atoi(optarg));
+			c.overprovision = atof(optarg);
+			break;
+		case 'O':
+			parse_feature(optarg);
 			break;
 		case 's':
-			config.segs_per_sec = atoi(optarg);
-			MSG(0, "Info: Segments per section = %d\n",
-								atoi(optarg));
+			c.segs_per_sec = atoi(optarg);
 			break;
 		case 'z':
-			config.secs_per_zone = atoi(optarg);
-			MSG(0, "Info: Sections per zone = %d\n", atoi(optarg));
+			c.secs_per_zone = atoi(optarg);
 			break;
 		case 't':
-			config.trim = atoi(optarg);
-			MSG(0, "Info: Trim is %s\n", config.trim ? "enabled": "disabled");
+			c.trim = atoi(optarg);
 			break;
 		default:
 			MSG(0, "\tError: Unknown option %c\n",option);
@@ -98,40 +139,56 @@
 		MSG(0, "\tError: Device not specified\n");
 		mkfs_usage();
 	}
-	config.device_name = argv[optind];
+
+	/* [0] : META, [1 to MAX_DEVICES - 1] : NODE/DATA */
+	c.devices[0].path = strdup(argv[optind]);
 
 	if ((optind + 1) < argc) {
-		/* We have a sector count. */
-		config.total_sectors = atoll(argv[optind+1]);
-		MSG(0, "\ttotal_sectors=%08"PRIx64" (%s bytes)\n",
-				config.total_sectors, argv[optind+1]);
+		if (c.ndevs > 1) {
+			MSG(0, "\tError: Not support custom size on multi-devs.\n");
+			mkfs_usage();
+		}
+		c.wanted_total_sectors = atoll(argv[optind+1]);
 	}
 
-	config.reserved_segments  =
-			(2 * (100 / config.overprovision + 1) + 6)
-			* config.segs_per_sec;
-	config.segs_per_zone = config.segs_per_sec * config.secs_per_zone;
+	if (c.zoned_mode)
+		c.feature |= cpu_to_le32(F2FS_FEATURE_BLKZONED);
 }
 
 int main(int argc, char *argv[])
 {
-	MSG(0, "\n\tF2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n",
-				F2FS_TOOLS_VERSION,
-				F2FS_TOOLS_DATE);
-	f2fs_init_configuration(&config);
+	f2fs_init_configuration();
 
 	f2fs_parse_options(argc, argv);
 
-	if (f2fs_dev_is_umounted(&config) < 0)
+	f2fs_show_info();
+
+	if (f2fs_devs_are_umounted() < 0) {
+		MSG(0, "\tError: Not available on mounted device!\n");
+		return -1;
+	}
+
+	if (f2fs_get_device_info() < 0)
 		return -1;
 
-	if (f2fs_get_device_info(&config) < 0)
+	/*
+	 * Some options are mandatory for host-managed
+	 * zoned block devices.
+	 */
+	if (c.zoned_model == F2FS_ZONED_HM && !c.zoned_mode) {
+		MSG(0, "\tError: zoned block device feature is required\n");
 		return -1;
+	}
+
+	if (c.zoned_mode && !c.trim) {
+		MSG(0, "\tError: Trim is required for zoned block devices\n");
+		return -1;
+	}
 
 	if (f2fs_format_device() < 0)
 		return -1;
 
-	f2fs_finalize_device(&config);
+	f2fs_finalize_device();
 
 	MSG(0, "Info: format successful\n");
 
diff --git a/mkfs/f2fs_format_utils.c b/mkfs/f2fs_format_utils.c
index a0f85f5..fc80ec6 100644
--- a/mkfs/f2fs_format_utils.c
+++ b/mkfs/f2fs_format_utils.c
@@ -27,38 +27,55 @@
 #include <linux/falloc.h>
 #endif
 
-int f2fs_trim_device()
+#ifndef BLKDISCARD
+#define BLKDISCARD	_IO(0x12,119)
+#endif
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD	_IO(0x12,125)
+#endif
+
+static int trim_device(int i)
 {
 	unsigned long long range[2];
 	struct stat stat_buf;
+	struct device_info *dev = c.devices + i;
+	u_int64_t bytes = dev->total_sectors * dev->sector_size;
+	int fd = dev->fd;
 
-	if (!config.trim)
-		return 0;
-
-	range[0] = 0;
-	range[1] = config.total_sectors * config.sector_size;
-
-	if (fstat(config.fd, &stat_buf) < 0 ) {
+	if (fstat(fd, &stat_buf) < 0 ) {
 		MSG(1, "\tError: Failed to get the device stat!!!\n");
 		return -1;
 	}
 
+	range[0] = 0;
+	range[1] = bytes;
+
 #if defined(WITH_BLKDISCARD) && defined(BLKDISCARD)
-	MSG(0, "Info: Discarding device\n");
+	MSG(0, "Info: [%s] Discarding device\n", dev->path);
 	if (S_ISREG(stat_buf.st_mode)) {
-#ifdef FALLOC_FL_PUNCH_HOLE
-		if (fallocate(config.fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
+		if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
 				range[0], range[1]) < 0) {
 			MSG(0, "Info: fallocate(PUNCH_HOLE|KEEP_SIZE) is failed\n");
 		}
 #endif
 		return 0;
 	} else if (S_ISBLK(stat_buf.st_mode)) {
-		if (ioctl(config.fd, BLKDISCARD, &range) < 0) {
-			MSG(0, "Info: This device doesn't support TRIM\n");
+		if (dev->zoned_model != F2FS_ZONED_NONE)
+			return f2fs_reset_zones(i);
+#ifdef BLKSECDISCARD
+		if (ioctl(fd, BLKSECDISCARD, &range) < 0) {
+			MSG(0, "Info: This device doesn't support BLKSECDISCARD\n");
 		} else {
-			MSG(0, "Info: Discarded %lu sectors\n",
-						config.total_sectors);
+			MSG(0, "Info: Secure Discarded %lu MB\n",
+						stat_buf.st_size >> 20);
+			return 0;
+		}
+#endif
+		if (ioctl(fd, BLKDISCARD, &range) < 0) {
+			MSG(0, "Info: This device doesn't support BLKDISCARD\n");
+		} else {
+			MSG(0, "Info: Discarded %llu MB\n", range[1] >> 20);
 		}
 	} else
 		return -1;
@@ -66,3 +83,12 @@
 	return 0;
 }
 
+int f2fs_trim_devices(void)
+{
+	int i;
+
+	for (i = 0; i < c.ndevs; i++)
+		if (trim_device(i))
+			return -1;
+	return 0;
+}
diff --git a/mkfs/f2fs_format_utils.h b/mkfs/f2fs_format_utils.h
index 9eb2cea..274db7b 100644
--- a/mkfs/f2fs_format_utils.h
+++ b/mkfs/f2fs_format_utils.h
@@ -10,7 +10,8 @@
 
 #include "f2fs_fs.h"
 
-extern struct f2fs_configuration config;
+extern struct f2fs_configuration c;
 
-int f2fs_trim_device(void);
+int f2fs_trim_device(int, u_int64_t);
+int f2fs_trim_devices(void);
 int f2fs_format_device(void);
diff --git a/scripts/verify.sh b/scripts/verify.sh
new file mode 100755
index 0000000..8a22a2d
--- /dev/null
+++ b/scripts/verify.sh
@@ -0,0 +1,137 @@
+#!/bin/bash
+
+IMG=../test.img
+TMP=/tmp/res
+XFSTESTS=~/xfstests
+TESTS="4 5 8 11 16 25 32 55 64"
+
+TARGET=./testdir
+MNT=/mnt/resize
+
+mkdir $TARGET 2>/dev/null
+mkdir $MNT 2>/dev/null
+
+umount $TARGET
+umount $MNT
+
+_check_out()
+{
+	if [ $1 -ne 0 ]; then
+		grep ASSERT $TMP
+		echo FAIL RETURN $1
+		exit
+	fi
+}
+
+_get_sec()
+{
+	echo $(($1*1024*1024*1024/512))
+}
+
+_mkfs()
+{
+	echo "========== Initialize $1 GB ============"
+	mkfs.f2fs $IMG `_get_sec $1` | grep sectors
+}
+
+_mount()
+{
+	echo "========== mount to $1 ================="
+	mount -t f2fs -o loop,discard,inline_data,inline_xattr $IMG $1 2>&1
+	_check_out $?
+}
+
+_fsck()
+{
+	echo "========== fsck.f2fs ==================="
+	fsck.f2fs $IMG -t 2>&1 >$TMP
+	_check_out $?
+	grep FSCK $TMP
+}
+
+_fsstress()
+{
+	echo "========== fsstress $1 ================="
+	$XFSTESTS/ltp/fsstress -x "echo 3 > /proc/sys/vm/drop_caches && sleep 1" -X 1 -r -f fsync=8 -f sync=0 -f write=8 -f dwrite=2 -f truncate=6 -f allocsp=0 -f bulkstat=0 -f bulkstat1=0 -f freesp=0 -f zero=1 -f collapse=1 -f insert=1 -f resvsp=0 -f unresvsp=0 -S t -p 10 -n $2 -d $1 >/dev/null
+}
+
+_resize()
+{
+	echo "========== resize.f2fs $1 GB ==========="
+	resize.f2fs -t `_get_sec $1` $IMG 2>&1 >$TMP
+	_check_out $?
+	_fsck
+}
+
+_resize_tests()
+{
+	for i in $TESTS
+	do
+		if [ $i -ge $1 ]; then
+			_resize $i
+		fi
+	done
+}
+
+_sload()
+{
+	echo "========== sload $1 ===================="
+	sload.f2fs -f $1 $IMG 2>&1
+	_check_out $?
+}
+
+from_mount()
+{
+	echo ""
+	echo " ****  $1 GB to $2 GB with $3 *** "
+	_mkfs $1
+	_mount $3
+	_fsstress $3 10000
+	umount $3
+	_fsck
+	_resize_tests $2
+}
+
+from_sload()
+{
+	echo ""
+	echo " ****  $1 GB to $2 GB with $3 *** "
+
+	_mkfs $1
+	_sload $3
+	_fsck
+
+	_mount $MNT
+	_fsstress $MNT 10000
+	umount $MNT
+	_fsck
+
+	_resize_tests $2
+
+	_mount $MNT
+	_fsstress $MNT 10000
+	umount $MNT
+	_fsck
+}
+
+test_all()
+{
+	for i in $TESTS
+	do
+		for j in $TESTS
+		do
+			if [ $i -lt $j ]; then
+				$1 $i $j $2
+			fi
+		done
+	done
+}
+
+test_all from_sload ~/grub
+
+rm -rf $TARGET/*
+_fsstress $TARGET 5000
+test_all from_sload $TARGET
+rm -rf $TARGET 2>/dev/null
+
+test_all from_mount $MNT
diff --git a/tools/fibmap.c b/tools/fibmap.c
index 224b233..6b092f5 100644
--- a/tools/fibmap.c
+++ b/tools/fibmap.c
@@ -8,6 +8,7 @@
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <libgen.h>
 #include <linux/hdreg.h>
 #include <linux/types.h>