[automerger skipped] Merge changes from topic "discard" into oc-dev am: 1cc112035f  -s ours
am: 8505f4f182  -s ours

Change-Id: I5d823e82145291fecc0005a183bcd6236a77cb70
diff --git a/.gitignore b/.gitignore
index debeffb..abe1336 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@
 /tools/f2fstat
 /tools/fibmap.f2fs
 /tools/parse.f2fs
+/tools/f2fscrypt
diff --git a/Android.mk b/Android.mk
index afd719b..d4f19cc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,19 +5,20 @@
 
 # The versions depend on $(LOCAL_PATH)/VERSION
 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)
+common_CFLAGS := -DWITH_ANDROID -DWITH_BLKDISCARD $(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/
+common_C_INCLUDES := $(LOCAL_PATH)/include external/e2fsprogs/lib/ system/core/libsparse/include
 
 #----------------------------------------------------------
 include $(CLEAR_VARS)
 LOCAL_MODULE := libf2fs_fmt
 LOCAL_SRC_FILES := \
 	lib/libf2fs.c \
+	lib/libf2fs_zoned.c \
 	mkfs/f2fs_format.c \
 	mkfs/f2fs_format_utils.c \
 
@@ -31,6 +32,7 @@
 LOCAL_MODULE := libf2fs_fmt_host
 LOCAL_SRC_FILES := \
 	lib/libf2fs.c \
+	lib/libf2fs_zoned.c \
 	mkfs/f2fs_format.c \
 	mkfs/f2fs_format_utils.c \
 
@@ -44,6 +46,7 @@
 LOCAL_MODULE := libf2fs_fmt_host_dyn
 LOCAL_SRC_FILES := \
 	lib/libf2fs.c \
+	lib/libf2fs_zoned.c \
 	lib/libf2fs_io.c \
 	mkfs/f2fs_format.c \
 	mkfs/f2fs_format_utils.c \
@@ -52,10 +55,10 @@
 LOCAL_CFLAGS := $(common_CFLAGS)
 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
 LOCAL_STATIC_LIBRARIES := \
-     libf2fs_ioutils_host \
-     libext2_uuid \
-     libsparse \
-     libz
+	libf2fs_ioutils_host \
+	libext2_uuid \
+	libsparse \
+	libz
 # LOCAL_LDLIBS := -ldl
 include $(BUILD_HOST_SHARED_LIBRARY)
 
@@ -74,7 +77,11 @@
 	mkfs/f2fs_format_main.c
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
 LOCAL_CFLAGS := $(target_CFLAGS)
-LOCAL_STATIC_LIBRARIES := libc libf2fs_fmt libext2_uuid
+LOCAL_STATIC_LIBRARIES := \
+	libf2fs_fmt \
+	libext2_uuid \
+	libsparse \
+	libz
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
 
@@ -88,13 +95,27 @@
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
 LOCAL_CFLAGS := $(target_CFLAGS)
 LOCAL_STATIC_LIBRARIES := libf2fs_fmt
-LOCAL_SHARED_LIBRARIES := libext2_uuid
+LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
 LOCAL_SYSTEM_SHARED_LIBRARIES := libc
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
 
 #----------------------------------------------------------
 include $(CLEAR_VARS)
+LOCAL_MODULE := make_f2fs
+
+LOCAL_SRC_FILES := \
+	mkfs/f2fs_format_main.c \
+	lib/libf2fs_io.c \
+
+LOCAL_C_INCLUDES := $(common_C_INCLUDES)
+LOCAL_CFLAGS := $(common_CFLAGS)
+LOCAL_STATIC_LIBRARIES := libf2fs_fmt_host
+LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
+include $(BUILD_HOST_EXECUTABLE)
+
+#----------------------------------------------------------
+include $(CLEAR_VARS)
 # The LOCAL_MODULE name is referenced by the code. Don't change it.
 LOCAL_MODULE := fsck.f2fs
 LOCAL_SRC_FILES := \
@@ -107,7 +128,7 @@
 
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
 LOCAL_CFLAGS := $(target_CFLAGS)
-LOCAL_SHARED_LIBRARIES := libext2_uuid
+LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
 LOCAL_SYSTEM_SHARED_LIBRARIES := libc
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
@@ -125,6 +146,7 @@
 
 LOCAL_C_INCLUDES := $(common_C_INCLUDES)
 LOCAL_CFLAGS := $(common_CFLAGS)
+LOCAL_SHARED_LIBRARIES := libsparse
 LOCAL_HOST_SHARED_LIBRARIES :=  libext2_uuid
 include $(BUILD_HOST_EXECUTABLE)
 
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..8256d56
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+deymo@google.com
+jaegeuk@google.com
+jinqian@google.com
diff --git a/configure.ac b/configure.ac
index 6a3f7c4..d6de43b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,8 +48,11 @@
 # Test configure options.
 AC_ARG_WITH([selinux],
 	AS_HELP_STRING([--without-selinux],
-	  [Ignore presence of libselinux and disable selinux support])
-)
+	  [Ignore presence of libselinux and disable selinux support]))
+
+AC_ARG_WITH([blkid],
+	AS_HELP_STRING([--without-blkid],
+	  [Ignore presence of libblkid and disable blkid support]))
 
 # Checks for programs.
 AC_PROG_CC
@@ -74,6 +77,19 @@
 	)]
 )
 
+AS_IF([test "x$with_blkid" != "xno"],
+	[PKG_CHECK_MODULES([libblkid], [blkid],
+	                   [have_blkid=yes], [have_blkid=no])],
+	[have_blkid=no]
+)
+
+AS_IF([test "x$have_blkid" = "xyes"],
+	[AC_DEFINE([HAVE_LIBBLKID], [1], [Use blkid])],
+	[AS_IF([test "x$with_blkid" = "xyes"],
+		[AC_MSG_ERROR([blkid support requested but libblkid not found])]
+	)]
+)
+
 # Checks for header files.
 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])
diff --git a/fsck/dir.c b/fsck/dir.c
index d817d27..57b7f9b 100644
--- a/fsck/dir.c
+++ b/fsck/dir.c
@@ -16,34 +16,6 @@
 #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;
@@ -136,14 +108,15 @@
 	int max_slots = 214;
 	nid_t ino = le32_to_cpu(dir->footer.ino);
 	f2fs_hash_t namehash;
+	unsigned int dir_level = dir->i.i_dir_level;
 	int ret = 0;
 
 	namehash = f2fs_dentry_hash(de->name, de->len);
 
-	nbucket = dir_buckets(level);
+	nbucket = dir_buckets(level, dir_level);
 	nblock = bucket_blocks(level);
 
-	bidx = dir_block_index(level, 0, le32_to_cpu(namehash) % nbucket);
+	bidx = dir_block_index(level, dir_level, le32_to_cpu(namehash) % nbucket);
 	end_block = bidx + nblock;
 
 	dentry_blk = calloc(BLOCK_SZ, 1);
@@ -185,12 +158,6 @@
 	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))
@@ -199,7 +166,7 @@
 	return 0;
 }
 
-static void f2fs_update_dentry(nid_t ino, umode_t mode,
+static void f2fs_update_dentry(nid_t ino, int file_type,
 		struct f2fs_dentry_ptr *d,
 		const unsigned char *name, int len, f2fs_hash_t name_hash,
 		unsigned int bit_pos)
@@ -214,7 +181,7 @@
 	memcpy(d->filename[bit_pos], name, len);
 	d->filename[bit_pos][len] = 0;
 	de->ino = cpu_to_le32(ino);
-	set_de_type(de, mode);
+	de->file_type = file_type;
 	for (i = 0; i < slots; i++)
 		test_and_set_bit_le(bit_pos + i, d->bitmap);
 }
@@ -223,23 +190,21 @@
  * 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)
+			const unsigned char *name, int name_len, nid_t ino,
+			int file_type, block_t p_blkaddr, int inc_link)
 {
 	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);
+	unsigned int dir_level = parent->i.i_dir_level;
 	int ret;
 
-	if (parent == NULL || child == NULL)
+	if (parent == NULL)
 		return -EINVAL;
 
 	if (!pino) {
@@ -262,9 +227,9 @@
 	if (level == current_depth)
 		++current_depth;
 
-	nbucket = dir_buckets(level);
+	nbucket = dir_buckets(level, dir_level);
 	nblock = bucket_blocks(level);
-	bidx = dir_block_index(level, 0, le32_to_cpu(dentry_hash) % nbucket);
+	bidx = dir_block_index(level, dir_level, le32_to_cpu(dentry_hash) % nbucket);
 
 	for (block = bidx; block <= (bidx + nblock - 1); block++) {
 
@@ -292,7 +257,7 @@
 
 add_dentry:
 	make_dentry_ptr(&d, (void *)dentry_blk, 1);
-	f2fs_update_dentry(ino, mode, &d, name, name_len, dentry_hash, bit_pos);
+	f2fs_update_dentry(ino, file_type, &d, name, name_len, dentry_hash, bit_pos);
 
 	ret = dev_write_block(dentry_blk, dn.data_blkaddr);
 	ASSERT(ret >= 0);
@@ -307,7 +272,7 @@
 	}
 
 	/* Update parent's i_links info*/
-	if (S_ISDIR(mode)) {
+	if (inc_link && (file_type == F2FS_FT_DIR)){
 		u32 links = le32_to_cpu(parent->i.i_links);
 		parent->i.i_links = cpu_to_le32(links + 1);
 		dn.idirty = 1;
@@ -471,6 +436,102 @@
 		page_symlink(sbi, node_blk, de->link, size);
 }
 
+int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
+							block_t p_blkaddr)
+{
+	struct f2fs_inode *inode = &(node->i);
+	unsigned int dir_level = node->i.i_dir_level;
+	nid_t ino = le32_to_cpu(node->footer.ino);
+	char inline_data[MAX_INLINE_DATA];
+	struct dnode_of_data dn = {0};
+	struct f2fs_dentry_ptr d;
+	unsigned long bit_pos = 0;
+	int ret = 0;
+
+	if (!(inode->i_inline & F2FS_INLINE_DENTRY))
+		return 0;
+
+	memcpy(inline_data, inline_data_addr(node), MAX_INLINE_DATA);
+	memset(inline_data_addr(node), 0, MAX_INLINE_DATA);
+	inode->i_inline &= ~F2FS_INLINE_DENTRY;
+
+	ret = dev_write_block(node, p_blkaddr);
+	ASSERT(ret >= 0);
+
+	if (!dir_level) {
+		struct f2fs_inline_dentry *inline_dentry;
+		struct f2fs_dentry_block *dentry_blk;
+
+		dentry_blk = calloc(BLOCK_SZ, 1);
+		ASSERT(dentry_blk);
+
+		set_new_dnode(&dn, node, NULL, ino);
+		get_dnode_of_data(sbi, &dn, 0, ALLOC_NODE);
+		if (dn.data_blkaddr == NULL_ADDR)
+			new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA);
+
+		inline_dentry = (struct f2fs_inline_dentry *)inline_data;
+		 /* copy data from inline dentry block to new dentry block */
+		memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap,
+				INLINE_DENTRY_BITMAP_SIZE);
+		memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0,
+			SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE);
+
+		memcpy(dentry_blk->dentry, inline_dentry->dentry,
+			sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY);
+		memcpy(dentry_blk->filename, inline_dentry->filename,
+				NR_INLINE_DENTRY * F2FS_SLOT_LEN);
+
+		ret = dev_write_block(dentry_blk, dn.data_blkaddr);
+		ASSERT(ret >= 0);
+
+		MSG(1, "%s: copy inline entry to block\n", __func__);
+
+		free(dentry_blk);
+		return ret;
+	}
+
+	make_empty_dir(sbi, node);
+	make_dentry_ptr(&d, (void *)inline_data, 2);
+
+	while (bit_pos < d.max) {
+		struct f2fs_dir_entry *de;
+		const unsigned char *filename;
+		int namelen;
+
+		if (!test_bit_le(bit_pos, d.bitmap)) {
+			bit_pos++;
+			continue;
+		}
+
+		de = &d.dentry[bit_pos];
+		if (!de->name_len) {
+			bit_pos++;
+			continue;
+		}
+
+		filename = d.filename[bit_pos];
+		namelen = le32_to_cpu(de->name_len);
+
+		if (is_dot_dotdot(filename, namelen)) {
+			bit_pos += GET_DENTRY_SLOTS(namelen);
+			continue;
+		}
+
+		ret = f2fs_add_link(sbi, node, filename, namelen,
+				le32_to_cpu(de->ino),
+				de->file_type, p_blkaddr, 0);
+		if (ret)
+			MSG(0, "Convert file \"%s\" ERR=%d\n", filename, ret);
+		else
+			MSG(1, "%s: add inline entry to block\n", __func__);
+
+		bit_pos += GET_DENTRY_SLOTS(namelen);
+	}
+
+	return 0;
+}
+
 int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
 {
 	struct f2fs_node *parent, *child;
@@ -492,6 +553,13 @@
 	ret = dev_read_block(parent, ni.blk_addr);
 	ASSERT(ret >= 0);
 
+	/* Must convert inline dentry before the following opertions */
+	ret = convert_inline_dentry(sbi, parent, ni.blk_addr);
+	if (ret) {
+		MSG(0, "Convert inline dentry for pino=%x failed.\n", de->pino);
+		return -1;
+	}
+
 	ret = f2fs_find_entry(sbi, parent, de);
 	if (ret) {
 		MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
@@ -500,13 +568,6 @@
 			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);
@@ -515,7 +576,11 @@
 
 	init_inode_block(sbi, child, de);
 
-	ret = f2fs_add_link(sbi, parent, child, ni.blk_addr);
+	ret = f2fs_add_link(sbi, parent, child->i.i_name,
+				le32_to_cpu(child->i.i_namelen),
+				le32_to_cpu(child->footer.ino),
+				map_de_type(le16_to_cpu(child->i.i_mode)),
+				ni.blk_addr, 1);
 	if (ret) {
 		MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
 					de->name, de->pino, ret);
diff --git a/fsck/dump.c b/fsck/dump.c
index 8cbeda1..22e2265 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -356,7 +356,7 @@
 	unsigned char name[F2FS_NAME_LEN + 1] = {0};
 	char path[1024] = {0};
 	char ans[255] = {0};
-	int is_encrypt = file_is_encrypt(inode);
+	int enc_name = file_enc_name(inode);
 	int ret;
 
 	if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
@@ -377,7 +377,7 @@
 
 		/* make a file */
 		namelen = convert_encrypted_name(inode->i_name, namelen,
-							name, is_encrypt);
+							name, enc_name);
 		name[namelen] = 0;
 		sprintf(path, "./lost_found/%s", name);
 
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index 5c8eea5..efc43f6 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -444,9 +444,9 @@
 	[S_IFLNK >> S_SHIFT]    = F2FS_FT_SYMLINK,
 };
 
-static inline void set_de_type(struct f2fs_dir_entry *de, umode_t mode)
+static inline int map_de_type(umode_t mode)
 {
-	de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
+       return f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
 }
 
 static inline void *inline_xattr_addr(struct f2fs_inode *inode)
@@ -465,4 +465,41 @@
 #define IS_SUM_NODE_SEG(footer)		(footer.entry_type == SUM_TYPE_NODE)
 #define IS_SUM_DATA_SEG(footer)		(footer.entry_type == SUM_TYPE_DATA)
 
+static inline 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 inline unsigned int bucket_blocks(unsigned int level)
+{
+	if (level < MAX_DIR_HASH_DEPTH / 2)
+		return 2;
+	else
+		return 4;
+}
+
+static inline 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 inline int is_dot_dotdot(const unsigned char *name, const int len)
+{
+	if (len == 1 && name[0] == '.')
+		return 1;
+	if (len == 2 && name[0] == '.' && name[1] == '.')
+		return 1;
+	return 0;
+}
+
 #endif /* _F2FS_H_ */
diff --git a/fsck/fsck.c b/fsck/fsck.c
index e97ee0a..56336ad 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -347,7 +347,7 @@
 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, u8 *name)
+			struct node_info *ni)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	int ret;
@@ -470,7 +470,7 @@
 
 	/* Sanity check */
 	if (sanity_check_nid(sbi, x_nid, node_blk,
-				F2FS_FT_XATTR, TYPE_XATTR, &ni, NULL)) {
+				F2FS_FT_XATTR, TYPE_XATTR, &ni)) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -484,7 +484,7 @@
 }
 
 int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
-		u32 nid, u8 *name, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
+		u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
 		u32 *blk_cnt, struct child_info *child)
 {
 	struct node_info ni;
@@ -493,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, name))
+	if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni))
 		goto err;
 
 	if (ntype == TYPE_INODE) {
@@ -720,7 +720,7 @@
 					blkaddr,
 					&child, (i_blocks == *blk_cnt),
 					ftype, nid, idx, ni->version,
-					file_is_encrypt(&node_blk->i));
+					file_enc_name(&node_blk->i));
 			if (!ret) {
 				*blk_cnt = *blk_cnt + 1;
 			} else if (c.fix_on) {
@@ -748,7 +748,7 @@
 			goto skip;
 
 		ret = fsck_chk_node_blk(sbi, &node_blk->i, i_nid,
-				NULL, ftype, ntype, blk_cnt, &child);
+					ftype, ntype, blk_cnt, &child);
 		if (!ret) {
 			*blk_cnt = *blk_cnt + 1;
 		} else if (ret == -EINVAL) {
@@ -793,7 +793,7 @@
 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, file_enc_name(&node_blk->i));
 	en[namelen] = '\0';
 	if (ftype == F2FS_FT_ORPHAN)
 		DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n",
@@ -878,7 +878,7 @@
 			blkaddr, child,
 			le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
 			nid, idx, ni->version,
-			file_is_encrypt(inode));
+			file_enc_name(inode));
 		if (!ret) {
 			*blk_cnt = *blk_cnt + 1;
 		} else if (c.fix_on) {
@@ -905,7 +905,7 @@
 		if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
 			goto skip;
 		ret = fsck_chk_node_blk(sbi, inode,
-				le32_to_cpu(node_blk->in.nid[i]), NULL,
+				le32_to_cpu(node_blk->in.nid[i]),
 				ftype, TYPE_DIRECT_NODE, blk_cnt, child);
 		if (!ret)
 			*blk_cnt = *blk_cnt + 1;
@@ -945,7 +945,7 @@
 		if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
 			goto skip;
 		ret = fsck_chk_node_blk(sbi, inode,
-				le32_to_cpu(node_blk->in.nid[i]), NULL,
+				le32_to_cpu(node_blk->in.nid[i]),
 				ftype, TYPE_INDIRECT_NODE, blk_cnt, child);
 		if (!ret)
 			*blk_cnt = *blk_cnt + 1;
@@ -1005,9 +1005,9 @@
 }
 
 int convert_encrypted_name(unsigned char *name, int len,
-				unsigned char *new, int encrypted)
+				unsigned char *new, int enc_name)
 {
-	if (!encrypted) {
+	if (!enc_name) {
 		memcpy(new, name, len);
 		new[len] = 0;
 		return len;
@@ -1019,7 +1019,7 @@
 
 static void print_dentry(__u32 depth, __u8 *name,
 		u8 *bitmap, struct f2fs_dir_entry *dentry,
-		int max, int idx, int last_blk, int encrypted)
+		int max, int idx, int last_blk, int enc_name)
 {
 	int last_de = 0;
 	int next_idx = 0;
@@ -1028,7 +1028,7 @@
 	int bit_offset;
 	unsigned char new[F2FS_NAME_LEN + 1];
 
-	if (c.dbg_lv != -1)
+	if (!c.show_dentry)
 		return;
 
 	name_len = le16_to_cpu(dentry[idx].name_len);
@@ -1056,16 +1056,16 @@
 	for (i = 1; i < depth; i++)
 		printf("%c   ", tree_mark[i]);
 
-	convert_encrypted_name(name, name_len, new, encrypted);
+	convert_encrypted_name(name, name_len, new, enc_name);
 
 	printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n",
 			last_de ? '`' : '|',
 			new, le32_to_cpu(dentry[idx].ino),
-			encrypted);
+			enc_name);
 }
 
 static int f2fs_check_hash_code(struct f2fs_dir_entry *dentry,
-			const unsigned char *name, u32 len, int encrypted)
+			const unsigned char *name, u32 len, int enc_name)
 {
 	f2fs_hash_t hash_code = f2fs_dentry_hash(name, len);
 
@@ -1074,7 +1074,7 @@
 		unsigned char new[F2FS_NAME_LEN + 1];
 
 		convert_encrypted_name((unsigned char *)name, len,
-							new, encrypted);
+							new, enc_name);
 		FIX_MSG("Mismatch hash_code for \"%s\" [%x:%x]",
 				new, le32_to_cpu(dentry->hash_code),
 				hash_code);
@@ -1084,33 +1084,6 @@
 	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)
 {
@@ -1156,7 +1129,7 @@
 			       struct child_info *child,
 			       u8 *name, int len,
 			       __u8 (*filename)[F2FS_SLOT_LEN],
-			       int encrypted)
+			       int enc_name)
 {
 	int fixed = 0;
 
@@ -1185,7 +1158,7 @@
 		}
 	}
 
-	if (f2fs_check_hash_code(dentry, name, len, encrypted))
+	if (f2fs_check_hash_code(dentry, name, len, enc_name))
 		fixed = 1;
 
 	if (name[len] != '\0') {
@@ -1208,7 +1181,7 @@
 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 encrypted)
+			int max, int last_blk, int enc_name)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	enum FILE_TYPE ftype;
@@ -1298,7 +1271,7 @@
 							name_len == 2)) {
 				ret = __chk_dots_dentries(sbi, &dentry[i],
 					child, name, name_len, &filenames[i],
-					encrypted);
+					enc_name);
 				switch (ret) {
 				case 1:
 					fixed = 1;
@@ -1321,7 +1294,7 @@
 			}
 		}
 
-		if (f2fs_check_hash_code(dentry + i, name, name_len, encrypted))
+		if (f2fs_check_hash_code(dentry + i, name, name_len, enc_name))
 			fixed = 1;
 
 		if (max == NR_DENTRY_IN_BLOCK) {
@@ -1340,7 +1313,7 @@
 			}
 		}
 
-		en_len = convert_encrypted_name(name, name_len, en, encrypted);
+		en_len = convert_encrypted_name(name, name_len, en, enc_name);
 		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, en, name_len,
@@ -1348,11 +1321,11 @@
 				dentry[i].file_type);
 
 		print_dentry(fsck->dentry_depth, name, bitmap,
-				dentry, max, i, last_blk, encrypted);
+				dentry, max, i, last_blk, enc_name);
 
 		blk_cnt = 1;
 		ret = fsck_chk_node_blk(sbi,
-				NULL, le32_to_cpu(dentry[i].ino), name,
+				NULL, le32_to_cpu(dentry[i].ino),
 				ftype, TYPE_INODE, &blk_cnt, NULL);
 
 		if (ret && c.fix_on) {
@@ -1393,7 +1366,7 @@
 			de_blk->dentry_bitmap,
 			de_blk->dentry, de_blk->filename,
 			NR_INLINE_DENTRY, 1,
-			file_is_encrypt(&node_blk->i));
+			file_enc_name(&node_blk->i));
 	if (dentries < 0) {
 		DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n",
 			fsck->dentry_depth);
@@ -1408,7 +1381,7 @@
 }
 
 int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
-		struct child_info *child, int last_blk, int encrypted)
+		struct child_info *child, int last_blk, int enc_name)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	struct f2fs_dentry_block *de_blk;
@@ -1424,7 +1397,7 @@
 	dentries = __chk_dentries(sbi, child,
 			de_blk->dentry_bitmap,
 			de_blk->dentry, de_blk->filename,
-			NR_DENTRY_IN_BLOCK, last_blk, encrypted);
+			NR_DENTRY_IN_BLOCK, last_blk, enc_name);
 
 	if (dentries < 0 && !c.ro) {
 		ret = dev_write_block(de_blk, blk_addr);
@@ -1445,7 +1418,7 @@
 int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
 		struct child_info *child, int last_blk,
 		enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver,
-		int encrypted)
+		int enc_name)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 
@@ -1479,7 +1452,7 @@
 	if (ftype == F2FS_FT_DIR) {
 		f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_HOT_DATA);
 		return fsck_chk_dentry_blk(sbi, blk_addr, child,
-						last_blk, encrypted);
+						last_blk, enc_name);
 	} else {
 		f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_WARM_DATA);
 	}
@@ -1528,7 +1501,7 @@
 				continue;
 			}
 
-			ret = fsck_chk_node_blk(sbi, NULL, ino, NULL,
+			ret = fsck_chk_node_blk(sbi, NULL, ino,
 					F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt,
 					NULL);
 			if (!ret)
@@ -1711,7 +1684,7 @@
 	while (node) {
 		/* Sanity check */
 		if (sanity_check_nid(sbi, node->nid, node_blk,
-					F2FS_FT_MAX, TYPE_INODE, &ni, NULL))
+					F2FS_FT_MAX, TYPE_INODE, &ni))
 			FIX_MSG("Failed to fix, rerun fsck.f2fs");
 
 		node_blk->i.i_links = cpu_to_le32(node->actual_links);
@@ -1779,9 +1752,11 @@
 		flags |= CP_ORPHAN_PRESENT_FLAG;
 	}
 
-	set_cp(ckpt_flags, flags);
 	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload));
 
+	flags = update_nat_bits_flags(sb, cp, flags);
+	set_cp(ckpt_flags, flags);
+
 	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);
@@ -1814,6 +1789,10 @@
 
 	ret = dev_write_block(cp, cp_blk_no++);
 	ASSERT(ret >= 0);
+
+	/* Write nat bits */
+	if (flags & CP_NAT_BITS_FLAG)
+		write_nat_bits(sbi, sb, cp, sbi->cur_cp);
 }
 
 int check_curseg_offset(struct f2fs_sb_info *sbi)
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 5a6f018..c54eccb 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -119,7 +119,7 @@
 
 extern int fsck_chk_orphan_node(struct f2fs_sb_info *);
 extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
-		u8 *, enum FILE_TYPE, enum NODE_TYPE, u32 *,
+		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 *);
@@ -178,6 +178,11 @@
 extern void rewrite_current_sit_page(struct f2fs_sb_info *, unsigned int,
 			struct f2fs_sit_block *);
 
+extern u32 update_nat_bits_flags(struct f2fs_super_block *,
+				struct f2fs_checkpoint *, u32);
+extern void write_nat_bits(struct f2fs_sb_info *, struct f2fs_super_block *,
+			struct f2fs_checkpoint *, int);
+
 /* dump.c */
 struct dump_option {
 	nid_t nid;
diff --git a/fsck/main.c b/fsck/main.c
index 6c94a70..c9411eb 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -29,7 +29,7 @@
 	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");
+	MSG(0, "  -t show directory tree\n");
 	exit(1);
 }
 
@@ -80,7 +80,7 @@
 
 static int is_digits(char *optarg)
 {
-	int i;
+	unsigned int i;
 
 	for (i = 0; i < strlen(optarg); i++)
 		if (!isdigit(optarg[i]))
@@ -88,18 +88,20 @@
 	return i == strlen(optarg);
 }
 
-static void error_out(void)
+static void error_out(char *prog)
 {
-	if (c.func == FSCK)
+	if (!strcmp("fsck.f2fs", prog))
 		fsck_usage();
-	else if (c.func == DUMP)
+	else if (!strcmp("dump.f2fs", prog))
 		dump_usage();
-	else if (c.func == DEFRAG)
+	else if (!strcmp("defrag.f2fs", prog))
 		defrag_usage();
-	else if (c.func == RESIZE)
+	else if (!strcmp("resize.f2fs", prog))
 		resize_usage();
-	else if (c.func == SLOAD)
+	else if (!strcmp("sload.f2fs", prog))
 		sload_usage();
+	else
+		MSG(0, "\nWrong progam.\n");
 }
 
 void f2fs_parse_options(int argc, char *argv[])
@@ -110,10 +112,8 @@
 
 	if (argc < 2) {
 		MSG(0, "\tError: Device not specified\n");
-		error_out();
+		error_out(prog);
 	}
-	c.devices[0].path = strdup(argv[argc - 1]);
-	argv[argc-- - 1] = 0;
 
 	if (!strcmp("fsck.f2fs", prog)) {
 		const char *option_string = ":ad:fp:t";
@@ -164,7 +164,7 @@
 				MSG(0, "Info: Force to fix corruption\n");
 				break;
 			case 't':
-				c.dbg_lv = -1;
+				c.show_dentry = 1;
 				break;
 
 
@@ -370,7 +370,14 @@
 				break;
 		}
 	}
-	if (argc > optind) {
+
+	if (optind >= argc) {
+		MSG(0, "\tError: Device not specified\n");
+		error_out(prog);
+	}
+
+	c.devices[0].path = strdup(argv[optind]);
+	if (argc > (optind + 1)) {
 		c.dbg_lv = 0;
 		err = EUNKNOWN_ARG;
 	}
@@ -392,7 +399,7 @@
 		MSG(0, "\tError: Unknown argument %s\n", argv[optind]);
 		break;
 	}
-	error_out();
+	error_out(prog);
 }
 
 static void do_fsck(struct f2fs_sb_info *sbi)
@@ -440,7 +447,7 @@
 
 	/* Traverse all block recursively from root inode */
 	blk_cnt = 1;
-	fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, (u8 *)"/",
+	fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
 			F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
 	fsck_verify(sbi);
 	fsck_free(sbi);
@@ -535,8 +542,9 @@
 		return -1;
 	}
 
-	if (c.target_sectors <=
-			(get_sb(block_count) << get_sb(log_sectors_per_block))) {
+	/* may different sector size */
+	if ((c.target_sectors * c.sector_size >>
+			get_sb(log_blocksize)) <= get_sb(block_count)) {
 		ASSERT_MSG("Nothing to resize, now only support resize to expand\n");
 		return -1;
 	}
@@ -566,6 +574,8 @@
 	f2fs_parse_options(argc, argv);
 
 	if (f2fs_devs_are_umounted() < 0) {
+		if (errno == EBUSY)
+			return -1;
 		if (!c.ro || c.func == DEFRAG) {
 			MSG(0, "\tError: Not available on mounted device!\n");
 			return -1;
diff --git a/fsck/mount.c b/fsck/mount.c
index b8e8c45..a0b0bea 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -40,14 +40,14 @@
 	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);
+	int enc_name = file_enc_name(inode);
 
-	namelen = convert_encrypted_name(inode->i_name, namelen, en, is_encrypt);
+	namelen = convert_encrypted_name(inode->i_name, namelen, en, enc_name);
 	en[namelen] = '\0';
 	if (name && namelen) {
 		inode->i_name[namelen] = '\0';
 		MSG(0, " - File name         : %s%s\n", en,
-				is_encrypt ? " <encrypted>" : "");
+				enc_name ? " <encrypted>" : "");
 		setlocale(LC_ALL, "");
 		MSG(0, " - File size         : %'llu (bytes)\n",
 				le64_to_cpu(inode->i_size));
@@ -257,6 +257,10 @@
 		MSG(0, "%s", " orphan_inodes");
 	if (flag & CP_FASTBOOT_FLAG)
 		MSG(0, "%s", " fastboot");
+	if (flag & CP_NAT_BITS_FLAG)
+		MSG(0, "%s", " nat_bits");
+	if (flag & CP_TRIMMED_FLAG)
+		MSG(0, "%s", " trimmed");
 	if (flag & CP_UMOUNT_FLAG)
 		MSG(0, "%s", " unmount");
 	else
@@ -545,7 +549,7 @@
 
 	cp = (struct f2fs_checkpoint *)cp_page_1;
 	crc_offset = get_cp(checksum_offset);
-	if (crc_offset >= blk_size)
+	if (crc_offset > (blk_size - sizeof(__le32)))
 		goto invalid_cp1;
 
 	crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
@@ -563,7 +567,7 @@
 
 	cp = (struct f2fs_checkpoint *)cp_page_2;
 	crc_offset = get_cp(checksum_offset);
-	if (crc_offset >= blk_size)
+	if (crc_offset > (blk_size - sizeof(__le32)))
 		goto invalid_cp2;
 
 	crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
@@ -754,6 +758,92 @@
 	return 0;
 }
 
+u32 update_nat_bits_flags(struct f2fs_super_block *sb,
+				struct f2fs_checkpoint *cp, u32 flags)
+{
+	u_int32_t nat_bits_bytes, nat_bits_blocks;
+
+	nat_bits_bytes = get_sb(segment_count_nat) << 5;
+	nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
+						F2FS_BLKSIZE - 1);
+	if (get_cp(cp_pack_total_block_count) <=
+			(1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks)
+		flags |= CP_NAT_BITS_FLAG;
+	else
+		flags &= (~CP_NAT_BITS_FLAG);
+
+	return flags;
+}
+
+/* should call flush_journal_entries() bfore this */
+void write_nat_bits(struct f2fs_sb_info *sbi,
+	struct f2fs_super_block *sb, struct f2fs_checkpoint *cp, int set)
+{
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	u_int32_t nat_blocks = get_sb(segment_count_nat) <<
+				(get_sb(log_blocks_per_seg) - 1);
+	u_int32_t nat_bits_bytes = nat_blocks >> 3;
+	u_int32_t nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) +
+					8 + F2FS_BLKSIZE - 1);
+	unsigned char *nat_bits, *full_nat_bits, *empty_nat_bits;
+	struct f2fs_nat_block *nat_block;
+	u_int32_t i, j;
+	block_t blkaddr;
+	int ret;
+
+	nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks);
+	ASSERT(nat_bits);
+
+	nat_block = malloc(F2FS_BLKSIZE);
+	ASSERT(nat_block);
+
+	full_nat_bits = nat_bits + 8;
+	empty_nat_bits = full_nat_bits + nat_bits_bytes;
+
+	memset(full_nat_bits, 0, nat_bits_bytes);
+	memset(empty_nat_bits, 0, nat_bits_bytes);
+
+	for (i = 0; i < nat_blocks; i++) {
+		int seg_off = i >> get_sb(log_blocks_per_seg);
+		int valid = 0;
+
+		blkaddr = (pgoff_t)(get_sb(nat_blkaddr) +
+				(seg_off << get_sb(log_blocks_per_seg) << 1) +
+				(i & ((1 << get_sb(log_blocks_per_seg)) - 1)));
+
+		if (f2fs_test_bit(i, nm_i->nat_bitmap))
+			blkaddr += (1 << get_sb(log_blocks_per_seg));
+
+		ret = dev_read_block(nat_block, blkaddr);
+		ASSERT(ret >= 0);
+
+		for (j = 0; j < NAT_ENTRY_PER_BLOCK; j++) {
+			if ((i == 0 && j == 0) ||
+				nat_block->entries[j].block_addr != NULL_ADDR)
+				valid++;
+		}
+		if (valid == 0)
+			test_and_set_bit_le(i, empty_nat_bits);
+		else if (valid == NAT_ENTRY_PER_BLOCK)
+			test_and_set_bit_le(i, full_nat_bits);
+	}
+	*(__le64 *)nat_bits = get_cp_crc(cp);
+	free(nat_block);
+
+	blkaddr = get_sb(segment0_blkaddr) + (set <<
+				get_sb(log_blocks_per_seg)) - nat_bits_blocks;
+
+	DBG(1, "\tWriting NAT bits pages, at offset 0x%08x\n", blkaddr);
+
+	for (i = 0; i < nat_bits_blocks; i++) {
+		if (dev_write_block(nat_bits + i * F2FS_BLKSIZE, blkaddr + i))
+			ASSERT_MSG("\tError: write NAT bits to disk!!!\n");
+	}
+	MSG(0, "Info: Write valid nat_bits in checkpoint\n");
+
+	free(nat_bits);
+}
+
 int init_node_manager(struct f2fs_sb_info *sbi)
 {
 	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
@@ -997,11 +1087,8 @@
 							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);
-	}
+	ret = dev_write_block(sum_blk, GET_SUM_BLKADDR(sbi, segno));
+	ASSERT(ret >= 0);
 
 	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
 					type == SEG_TYPE_MAX)
@@ -1794,12 +1881,13 @@
 		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));
 
+	flags = update_nat_bits_flags(sb, cp, flags);
+	set_cp(ckpt_flags, flags);
+
 	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
 	*((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
 
@@ -1833,6 +1921,10 @@
 	/* write the last cp */
 	ret = dev_write_block(cp, cp_blk_no++);
 	ASSERT(ret >= 0);
+
+	/* Write nat bits */
+	if (flags & CP_NAT_BITS_FLAG)
+		write_nat_bits(sbi, sb, cp, sbi->cur_cp);
 }
 
 void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
@@ -2008,6 +2100,7 @@
 int f2fs_do_mount(struct f2fs_sb_info *sbi)
 {
 	struct f2fs_checkpoint *cp = NULL;
+	struct f2fs_super_block *sb = NULL;
 	int ret;
 
 	sbi->active_logs = NR_CURSEG_TYPE;
@@ -2017,12 +2110,13 @@
 		if (ret)
 			return -1;
 	}
+	sb = F2FS_RAW_SUPER(sbi);
 
-	ret = check_sector_size(sbi->raw_super);
+	ret = check_sector_size(sb);
 	if (ret)
 		return -1;
 
-	print_raw_sb_info(F2FS_RAW_SUPER(sbi));
+	print_raw_sb_info(sb);
 
 	init_sb_info(sbi);
 
@@ -2068,6 +2162,30 @@
 		return -1;
 	}
 
+	/* Check nat_bits */
+	if (is_set_ckpt_flags(cp, CP_NAT_BITS_FLAG)) {
+		u_int32_t nat_bits_bytes, nat_bits_blocks;
+		__le64 *kaddr;
+		u_int32_t blk;
+
+		blk = get_sb(cp_blkaddr) + (1 << get_sb(log_blocks_per_seg));
+		if (sbi->cur_cp == 2)
+			blk += 1 << get_sb(log_blocks_per_seg);
+
+		nat_bits_bytes = get_sb(segment_count_nat) << 5;
+		nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
+				F2FS_BLKSIZE - 1);
+		blk -= nat_bits_blocks;
+
+		kaddr = malloc(PAGE_SIZE);
+		ret = dev_read_block(kaddr, blk);
+		ASSERT(ret >= 0);
+		if (*kaddr != get_cp_crc(cp))
+			write_nat_bits(sbi, sb, cp, sbi->cur_cp);
+		else
+			MSG(0, "Info: Found valid nat_bits in checkpoint\n");
+		free(kaddr);
+	}
 	return 0;
 }
 
diff --git a/fsck/resize.c b/fsck/resize.c
index 29cb1c0..4584d6f 100644
--- a/fsck/resize.c
+++ b/fsck/resize.c
@@ -415,6 +415,7 @@
 			struct f2fs_super_block *new_sb, unsigned int offset)
 {
 	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	unsigned long long cp_ver = get_cp(checkpoint_ver);
 	struct f2fs_checkpoint *new_cp;
 	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	unsigned int free_segment_count, new_segment_count;
@@ -422,6 +423,7 @@
 	block_t orphan_blks = 0;
 	block_t new_cp_blk_no, old_cp_blk_no;
 	u_int32_t crc = 0;
+	u32 flags;
 	void *buf;
 	int i, ret;
 
@@ -474,11 +476,17 @@
 			((get_newsb(segment_count_nat) / 2) <<
 			get_newsb(log_blocks_per_seg)) / 8);
 
+	/* update nat_bits flag */
+	flags = update_nat_bits_flags(new_sb, cp, get_cp(ckpt_flags));
+	set_cp(ckpt_flags, flags);
+
 	memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
 						(unsigned char *)cp);
+	new_cp->checkpoint_ver = cpu_to_le64(cp_ver + 1);
 
 	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
-	*((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+	*((__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);
@@ -519,6 +527,10 @@
 	ret = dev_write_block(new_cp, new_cp_blk_no++);
 	ASSERT(ret >= 0);
 
+	/* Write nat bits */
+	if (flags & CP_NAT_BITS_FLAG)
+		write_nat_bits(sbi, new_sb, new_cp, sbi->cur_cp == 1 ? 2 : 1);
+
 	/* disable old checkpoint */
 	memset(buf, 0, BLOCK_SZ);
 	ret = dev_write_block(buf, old_cp_blk_no);
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 97ee297..dd2635b 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -256,6 +256,7 @@
 struct f2fs_configuration {
 	u_int32_t reserved_segments;
 	u_int32_t new_reserved_segments;
+	int sparse_mode;
 	int zoned_mode;
 	int zoned_model;
 	size_t zone_blocks;
@@ -268,6 +269,7 @@
 	u_int32_t start_sector;
 	u_int32_t total_segments;
 	u_int32_t sector_size;
+	u_int64_t device_size;
 	u_int64_t total_sectors;
 	u_int64_t wanted_total_sectors;
 	u_int64_t target_sectors;
@@ -285,7 +287,9 @@
 	char *extension_list;
 	const char *rootdev_name;
 	int dbg_lv;
+	int show_dentry;
 	int trim;
+	int trimmed;
 	int func;
 	void *private;
 	int fix_on;
@@ -304,7 +308,7 @@
 	/* sload parameters */
 	char *from_dir;
 	char *mount_point;
-} __attribute__((packed));
+};
 
 #ifdef CONFIG_64BIT
 #define BITS_PER_LONG	64
@@ -514,6 +518,9 @@
 /*
  * For checkpoint
  */
+#define CP_TRIMMED_FLAG		0x00000100
+#define CP_NAT_BITS_FLAG	0x00000080
+#define CP_CRC_RECOVERY_FLAG	0x00000040
 #define CP_FASTBOOT_FLAG	0x00000020
 #define CP_FSCK_FLAG		0x00000010
 #define CP_ERROR_FLAG		0x00000008
@@ -610,8 +617,10 @@
 #define FADVISE_COLD_BIT       0x01
 #define FADVISE_LOST_PINO_BIT  0x02
 #define FADVISE_ENCRYPT_BIT    0x04
+#define FADVISE_ENC_NAME_BIT   0x08
 
 #define file_is_encrypt(fi)      ((fi)->i_advise & FADVISE_ENCRYPT_BIT)
+#define file_enc_name(fi)        ((fi)->i_advise & FADVISE_ENC_NAME_BIT)
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
@@ -1083,4 +1092,15 @@
 	return max_ovp;
 }
 
+static inline __le64 get_cp_crc(struct f2fs_checkpoint *cp)
+{
+	u_int64_t cp_ver = get_cp(checkpoint_ver);
+	size_t crc_offset = get_cp(checksum_offset);
+	u_int32_t crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp +
+							crc_offset));
+
+	cp_ver |= ((u_int64_t)crc << 32);
+	return cpu_to_le64(cp_ver);
+}
+
 #endif	/*__F2FS_FS_H */
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 93d3da9..31836db 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -32,6 +32,10 @@
 #define MODELINQUIRY	0x12,0x00,0x00,0x00,0x4A,0x00
 #endif
 
+#ifndef _WIN32 /* O_BINARY is windows-specific flag */
+#define O_BINARY 0
+#endif
+
 /*
  * UTF conversion codes are Copied from exfat tools.
  */
@@ -568,9 +572,10 @@
 	c.segs_per_sec = 1;
 	c.secs_per_zone = 1;
 	c.segs_per_zone = 1;
-	c.heap = 1;
+	c.heap = 0;
 	c.vol_label = "";
 	c.trim = 1;
+	c.trimmed = 0;
 	c.ro = 0;
 	c.kd = -1;
 }
@@ -688,7 +693,11 @@
 #endif
 	struct device_info *dev = c.devices + i;
 
-	fd = open((char *)dev->path, O_RDWR);
+	if (c.sparse_mode) {
+		fd = open((char *)dev->path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+	} else {
+		fd = open((char *)dev->path, O_RDWR);
+	}
 	if (fd < 0) {
 		MSG(0, "\tError: Failed to open the device!\n");
 		return -1;
@@ -709,7 +718,9 @@
 		return -1;
 	}
 
-	if (S_ISREG(stat_buf.st_mode)) {
+	if (c.sparse_mode) {
+		dev->total_sectors = c.device_size / dev->sector_size;
+	} else if (S_ISREG(stat_buf.st_mode)) {
 		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)
@@ -730,10 +741,12 @@
 #endif
 		dev->total_sectors /= dev->sector_size;
 
-		if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
-			c.start_sector = 0;
-		else
-			c.start_sector = geom.start;
+		if (i == 0) {
+			if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
+				c.start_sector = 0;
+			else
+				c.start_sector = geom.start;
+		}
 
 #ifndef WITH_ANDROID
 		/* Send INQUIRY command */
diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
index c09db36..aa99068 100644
--- a/lib/libf2fs_io.c
+++ b/lib/libf2fs_io.c
@@ -25,6 +25,19 @@
 
 struct f2fs_configuration c;
 
+#ifdef WITH_ANDROID
+#include <sparse/sparse.h>
+struct sparse_file *f2fs_sparse_file;
+
+struct buf_item {
+	void *buf;
+	size_t len;
+	struct buf_item *next;
+};
+
+struct buf_item *buf_list;
+#endif
+
 static int __get_device_fd(__u64 *offset)
 {
 	__u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS;
@@ -46,6 +59,8 @@
  */
 int dev_read_version(void *buf, __u64 offset, size_t len)
 {
+	if (c.sparse_mode)
+		return 0;
 	if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0)
 		return -1;
 	if (read(c.kd, buf, len) < 0)
@@ -55,8 +70,12 @@
 
 int dev_read(void *buf, __u64 offset, size_t len)
 {
-	int fd = __get_device_fd(&offset);
+	int fd;
 
+	if (c.sparse_mode)
+		return 0;
+
+	fd = __get_device_fd(&offset);
 	if (fd < 0)
 		return fd;
 
@@ -80,10 +99,40 @@
 #endif
 }
 
+#ifdef WITH_ANDROID
+static int dev_write_sparse(void *buf, __u64 byte_offset, size_t byte_len)
+{
+	struct buf_item *bi = calloc(1, sizeof(struct buf_item));
+
+	if (bi == NULL) {
+		return -1;
+	}
+	bi->buf = malloc(byte_len);
+	if (bi->buf == NULL) {
+		free(bi);
+		return -1;
+	}
+
+	bi->len = byte_len;
+	memcpy(bi->buf, buf, byte_len);
+	bi->next = buf_list;
+	buf_list = bi;
+
+	sparse_file_add_data(f2fs_sparse_file, bi->buf, byte_len, byte_offset/F2FS_BLKSIZE);
+	return 0;
+}
+#else
+static int dev_write_sparse(void *buf, __u64 byte_offset, size_t byte_len) { return 0; }
+#endif
+
 int dev_write(void *buf, __u64 offset, size_t len)
 {
-	int fd = __get_device_fd(&offset);
+	int fd;
 
+	if (c.sparse_mode)
+		return dev_write_sparse(buf, offset, len);
+
+	fd = __get_device_fd(&offset);
 	if (fd < 0)
 		return fd;
 
@@ -110,8 +159,12 @@
 
 int dev_fill(void *buf, __u64 offset, size_t len)
 {
-	int fd = __get_device_fd(&offset);
+	int fd;
 
+	if (c.sparse_mode)
+		return 0;
+
+	fd = __get_device_fd(&offset);
 	if (fd < 0)
 		return fd;
 
@@ -144,6 +197,20 @@
 {
 	int i;
 
+#ifdef WITH_ANDROID
+	if (c.sparse_mode) {
+		sparse_file_write(f2fs_sparse_file, c.devices[0].fd, /*gzip*/0, /*sparse*/1, /*crc*/0);
+		sparse_file_destroy(f2fs_sparse_file);
+		while (buf_list) {
+			struct buf_item *bi = buf_list;
+			buf_list = buf_list->next;
+			free(bi->buf);
+			free(bi);
+		}
+		f2fs_sparse_file = NULL;
+	}
+#endif
+
 	/*
 	 * We should call fsync() to flush out all the dirty pages
 	 * in the block device page cache.
diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
index bdb9755..c2f9c86 100644
--- a/man/mkfs.f2fs.8
+++ b/man/mkfs.f2fs.8
@@ -16,29 +16,46 @@
 .I device
 ]
 [
+.B \-d
+.I debugging-level
+]
+[
+.B \-e
+.I extension-list
+]
+[
+.B \-f
+]
+[
 .B \-l
 .I volume-label
 ]
 [
+.B \-m
+]
+[
 .B \-o
 .I overprovision-ratio-percentage
 ]
 [
+.B \-O
+.I feature-list
+]
+[
+.B \-q
+]
+[
 .B \-s
 .I #-of-segments-per-section
 ]
 [
+.B \-t
+.I nodiscard/discard
+]
+[
 .B \-z
 .I #-of-sections-per-zone
 ]
-[
-.B \-e
-.I extenstion-list
-]
-[
-.B \-d
-.I debugging-level
-]
 .I device
 .I [sectors]
 .SH DESCRIPTION
@@ -63,23 +80,9 @@
 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. If not specified, the best
-number will be assigned automatically accoring to the partition size.
-.TP
-.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 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.
-F2FS allocates segments for active logs with separated zones as much as possible.
-The default number is 1, which means a zone consists of one section.
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
 .TP
 .BI \-e " extension-list"
 Specify a file extension list in order f2fs to treat them as cold files.
@@ -87,9 +90,45 @@
 The default list includes most of multimedia file extensions such as jpg, gif,
 mpeg, mkv, and so on.
 .TP
-.BI \-d " debug-level"
-Specify the level of debugging options.
-The default number is 0, which shows basic debugging messages.
+.BI \-f
+Force overwrite when an existing filesystem is detected on the device.
+By default, mkfs.f2fs will not write to the device if it suspects that
+there is a filesystem or partition table on the device already.
+.TP
+.BI \-l " volume-label"
+Specify the volume label to the partition mounted as F2FS.
+.TP
+.BI \-m
+Specify f2fs filesystem to supports the block zoned feature.
+Without it, the filesystem isn't supports the feature.
+.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. If not specified, the best
+number will be assigned automatically accoring to the partition size.
+.TP
+.BI \-O " feature-list"
+Specify a feature list in order f2fs filesystem will supports.
+e.g "encrypt" and so on.
+.TP
+.BI \-q
+Quiet mode.
+With it, mkfs.f2fs does not show any messages include the basic messages.
+.TP
+.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 1, which means one segment is assigned to a section.
+.TP
+.BI \-t " nodiscard/discard"
+Specify 1 or 0 to enable/disable discard policy.
+If the value is equal to 1, discard policy is enabled, otherwise is disable.
+The default value is 1.
+.TP
+.BI \-z " #-of-sections-per-zone"
+Specify the number of sections per zone. A zone consists of multiple sections.
+F2FS allocates segments for active logs with separated zones as much as possible.
+The default number is 1, which means a zone consists of one section.
 .TP
 .SH AUTHOR
 This version of
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index 8b4c16c..0ea8b49 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -1,14 +1,14 @@
 ## Makefile.am
 
-AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
+AM_CPPFLAGS = ${libuuid_CFLAGS} ${libblkid_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 f2fs_format_utils.h $(top_srcdir)/include/f2fs_fs.h
-mkfs_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+mkfs_f2fs_LDADD = ${libuuid_LIBS} ${libblkid_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 \
+libf2fs_format_la_LDFLAGS = -luuid -L$(top_builddir)/lib -lf2fs \
 	-version-info $(FMT_CURRENT):$(FMT_REVISION):$(FMT_AGE)
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index 3c13026..ff1153a 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -117,6 +117,24 @@
 	free(c.extension_list);
 }
 
+static void verify_cur_segs(void)
+{
+	int i, j;
+
+	for (i = 0; i < NR_CURSEG_TYPE; i++) {
+		for (j = 0; j < NR_CURSEG_TYPE; j++)
+			if (c.cur_seg[i] == c.cur_seg[j])
+				break;
+	}
+
+	if (i == NR_CURSEG_TYPE && j == NR_CURSEG_TYPE)
+		return;
+
+	c.cur_seg[0] = 0;
+	for (i = 1; i < NR_CURSEG_TYPE; i++)
+		c.cur_seg[i] = next_zone(i - 1);
+}
+
 static int f2fs_prepare_super_block(void)
 {
 	u_int32_t blk_size_bytes;
@@ -337,10 +355,6 @@
 	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) ||
@@ -349,6 +363,10 @@
 		return -1;
 	}
 
+	c.reserved_segments =
+			(2 * (100 / c.overprovision + 1) + 6)
+			* c.segs_per_sec;
+
 	uuid_generate(sb->uuid);
 
 	utf8_to_utf16(sb->volume_name, (const char *)c.vol_label,
@@ -376,10 +394,17 @@
 		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);
+		c.cur_seg[CURSEG_COLD_DATA] =
+				max(last_zone((total_zones >> 2)),
+					next_zone(CURSEG_COLD_NODE));
+		c.cur_seg[CURSEG_WARM_DATA] =
+				max(last_zone((total_zones >> 1)),
+					next_zone(CURSEG_COLD_DATA));
 	}
 
+	/* if there is redundancy, reassign it */
+	verify_cur_segs();
+
 	cure_extension_list();
 
 	/* get kernel version */
@@ -472,8 +497,10 @@
 	struct f2fs_summary_block *sum = NULL;
 	struct f2fs_journal *journal;
 	u_int32_t blk_size_bytes;
+	u_int32_t nat_bits_bytes, nat_bits_blocks;
+	unsigned char *nat_bits = NULL, *empty_nat_bits;
 	u_int64_t cp_seg_blk = 0;
-	u_int32_t crc = 0;
+	u_int32_t crc = 0, flags;
 	unsigned int i;
 	char *cp_payload = NULL;
 	char *sum_compact, *sum_compact_p;
@@ -499,10 +526,19 @@
 	}
 	sum_compact_p = sum_compact;
 
+	nat_bits_bytes = get_sb(segment_count_nat) << 5;
+	nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
+						F2FS_BLKSIZE - 1);
+	nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks);
+	if (nat_bits == NULL) {
+		MSG(1, "\tError: Calloc Failed for nat bits buffer!!!\n");
+		goto free_sum_compact;
+	}
+
 	cp_payload = calloc(F2FS_BLKSIZE, 1);
 	if (cp_payload == NULL) {
 		MSG(1, "\tError: Calloc Failed for cp_payload!!!\n");
-		goto free_sum_compact;
+		goto free_nat_bits;
 	}
 
 	/* 1. cp page 1 of checkpoint pack 1 */
@@ -539,7 +575,15 @@
 			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);
+	flags = CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG;
+	if (get_cp(cp_pack_total_block_count) <=
+			(1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks)
+		flags |= CP_NAT_BITS_FLAG;
+
+	if (c.trimmed)
+		flags |= CP_TRIMMED_FLAG;
+
+	set_cp(ckpt_flags, flags);
 	set_cp(cp_pack_start_sum, 1 + get_sb(cp_payload));
 	set_cp(valid_node_count, 1);
 	set_cp(valid_inode_count, 1);
@@ -702,6 +746,31 @@
 		goto free_cp_payload;
 	}
 
+	/* write NAT bits, if possible */
+	if (flags & CP_NAT_BITS_FLAG) {
+		uint32_t i;
+
+		*(__le64 *)nat_bits = get_cp_crc(cp);
+		empty_nat_bits = nat_bits + 8 + nat_bits_bytes;
+		memset(empty_nat_bits, 0xff, nat_bits_bytes);
+		test_and_clear_bit_le(0, empty_nat_bits);
+
+		/* write the last blocks in cp pack */
+		cp_seg_blk = get_sb(segment0_blkaddr) + (1 <<
+				get_sb(log_blocks_per_seg)) - nat_bits_blocks;
+
+		DBG(1, "\tWriting NAT bits pages, at offset 0x%08"PRIx64"\n",
+					cp_seg_blk);
+
+		for (i = 0; i < nat_bits_blocks; i++) {
+			if (dev_write_block(nat_bits + i *
+						F2FS_BLKSIZE, cp_seg_blk + i)) {
+				MSG(1, "\tError: write NAT bits to disk!!!\n");
+				goto free_cp_payload;
+			}
+		}
+	}
+
 	/* cp page 1 of check point pack 2
 	 * Initiatialize other checkpoint pack with version zero
 	 */
@@ -741,6 +810,8 @@
 
 free_cp_payload:
 	free(cp_payload);
+free_nat_bits:
+	free(nat_bits);
 free_sum_compact:
 	free(sum_compact);
 free_sum:
@@ -775,6 +846,12 @@
 #ifndef WITH_ANDROID
 static int discard_obsolete_dnode(struct f2fs_node *raw_node, u_int64_t offset)
 {
+	u_int64_t next_blkaddr = 0;
+	u_int64_t root_inode_pos = get_sb(main_blkaddr);
+
+	/* only root inode was written before truncating dnodes */
+	root_inode_pos += c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg;
+
 	if (c.zoned_mode)
 		return 0;
 	do {
@@ -787,6 +864,7 @@
 			return -1;
 		}
 
+		next_blkaddr = le32_to_cpu(raw_node->footer.next_blkaddr);
 		memset(raw_node, 0, F2FS_BLKSIZE);
 
 		DBG(1, "\tDiscard dnode, at offset 0x%08"PRIx64"\n", offset);
@@ -794,7 +872,10 @@
 			MSG(1, "\tError: While discarding direct node!!!\n");
 			return -1;
 		}
-		offset = le32_to_cpu(raw_node->footer.next_blkaddr);
+		offset = next_blkaddr;
+		/* should avoid recursive chain due to stale data */
+		if (offset == root_inode_pos)
+			break;
 	} while (1);
 
 	return 0;
diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c
index 5bb1faf..5525d1c 100644
--- a/mkfs/f2fs_format_main.c
+++ b/mkfs/f2fs_format_main.c
@@ -17,28 +17,42 @@
 #include <sys/mount.h>
 #include <time.h>
 #include <uuid/uuid.h>
+#include <errno.h>
+
+#include "config.h"
+#ifdef HAVE_LIBBLKID
+#  include <blkid/blkid.h>
+#endif
 
 #include "f2fs_fs.h"
 #include "f2fs_format_utils.h"
 
+#ifdef WITH_ANDROID
+#include <sparse/sparse.h>
+extern struct sparse_file *f2fs_sparse_file;
+#endif
+
 extern struct f2fs_configuration c;
+static int force_overwrite = 0;
 
 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, "  -a heap-based allocation [default:0]\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, "  -f force overwrite the exist filesystem\n");
 	MSG(0, "  -l label\n");
+	MSG(0, "  -m support zoned block device [default:0]\n");
 	MSG(0, "  -o overprovision ratio [default:5]\n");
-	MSG(0, "  -O set feature\n");
+	MSG(0, "  -O [feature list] e.g. \"encrypt\"\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, "  -S sparse mode\n");
 	MSG(0, "  -t 0: nodiscard, 1: discard [default:1]\n");
-	MSG(0, "  -m support zoned block device [default:0]\n");
+	MSG(0, "  -z # of sections per zone [default:1]\n");
 	MSG(0, "sectors: number of sectors. [default: determined by device size]\n");
 	exit(1);
 }
@@ -62,6 +76,8 @@
 
 static void parse_feature(const char *features)
 {
+	while (*features == ' ')
+		features++;
 	if (!strcmp(features, "encrypt")) {
 		c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
 	} else {
@@ -72,7 +88,7 @@
 
 static void f2fs_parse_options(int argc, char *argv[])
 {
-	static const char *option_string = "qa:c:d:e:l:mo:O:s:z:t:";
+	static const char *option_string = "qa:c:d:e:l:mo:O:s:S:z:t:f";
 	int32_t option=0;
 
 	while ((option = getopt(argc,argv,option_string)) != EOF) {
@@ -122,12 +138,20 @@
 		case 's':
 			c.segs_per_sec = atoi(optarg);
 			break;
+		case 'S':
+			c.device_size = atoll(optarg);
+			c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1)));
+			c.sparse_mode = 1;
+			break;
 		case 'z':
 			c.secs_per_zone = atoi(optarg);
 			break;
 		case 't':
 			c.trim = atoi(optarg);
 			break;
+		case 'f':
+			force_overwrite = 1;
+			break;
 		default:
 			MSG(0, "\tError: Unknown option %c\n",option);
 			mkfs_usage();
@@ -151,10 +175,86 @@
 		c.wanted_total_sectors = atoll(argv[optind+1]);
 	}
 
+	if (c.sparse_mode)
+		c.trim = 0;
+
 	if (c.zoned_mode)
 		c.feature |= cpu_to_le32(F2FS_FEATURE_BLKZONED);
 }
 
+#ifdef HAVE_LIBBLKID
+static int f2fs_dev_is_overwrite(const char *device)
+{
+	const char	*type;
+	blkid_probe	pr = NULL;
+	int		ret = -1;
+
+	if (!device || !*device)
+		return 0;
+
+	pr = blkid_new_probe_from_filename(device);
+	if (!pr)
+		goto out;
+
+	ret = blkid_probe_enable_partitions(pr, 1);
+	if (ret < 0)
+		goto out;
+
+	ret = blkid_do_fullprobe(pr);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * Blkid returns 1 for nothing found and 0 when it finds a signature,
+	 * but we want the exact opposite, so reverse the return value here.
+	 *
+	 * In addition print some useful diagnostics about what actually is
+	 * on the device.
+	 */
+	if (ret) {
+		ret = 0;
+		goto out;
+	}
+
+	if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
+		MSG(0, "\t%s appears to contain an existing filesystem (%s).\n",
+			device, type);
+	} else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
+		MSG(0, "\t%s appears to contain a partition table (%s).\n",
+			device, type);
+	} else {
+		MSG(0, "\t%s appears to contain something weird according to blkid\n",
+			device);
+	}
+	ret = 1;
+out:
+	if (pr)
+		blkid_free_probe(pr);
+	if (ret == -1)
+		MSG(0, "\tprobe of %s failed, cannot detect existing filesystem.\n",
+			device);
+	return ret;
+}
+
+static int f2fs_check_overwrite(void)
+{
+	int i;
+
+	for (i = 0; i < c.ndevs; i++)
+		if (f2fs_dev_is_overwrite((char *)c.devices[i].path))
+			return -1;
+	return 0;
+}
+
+#else
+
+static int f2fs_check_overwrite(void)
+{
+	return 0;
+}
+
+#endif /* HAVE_LIBBLKID */
+
 int main(int argc, char *argv[])
 {
 	f2fs_init_configuration();
@@ -163,8 +263,14 @@
 
 	f2fs_show_info();
 
+	if (!force_overwrite && f2fs_check_overwrite()) {
+		MSG(0, "\tUse the -f option to force overwrite.\n");
+		return -1;
+	}
+
 	if (f2fs_devs_are_umounted() < 0) {
-		MSG(0, "\tError: Not available on mounted device!\n");
+		if (errno != EBUSY)
+			MSG(0, "\tError: Not available on mounted device!\n");
 		return -1;
 	}
 
@@ -185,6 +291,17 @@
 		return -1;
 	}
 
+	if (c.sparse_mode) {
+#ifndef WITH_ANDROID
+		MSG(0, "\tError: Sparse mode is only supported for android\n");
+		return -1;
+#else
+		if (f2fs_sparse_file)
+			sparse_file_destroy(f2fs_sparse_file);
+		f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size);
+#endif
+	}
+
 	if (f2fs_format_device() < 0)
 		return -1;
 
diff --git a/mkfs/f2fs_format_utils.c b/mkfs/f2fs_format_utils.c
index fc80ec6..558684d 100644
--- a/mkfs/f2fs_format_utils.c
+++ b/mkfs/f2fs_format_utils.c
@@ -90,5 +90,6 @@
 	for (i = 0; i < c.ndevs; i++)
 		if (trim_device(i))
 			return -1;
+	c.trimmed = 1;
 	return 0;
 }
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 69a0bb1..5a9303f 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -2,7 +2,10 @@
 
 AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
 AM_CFLAGS = -Wall
-sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs
+sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs f2fscrypt
 f2fstat_SOURCES = f2fstat.c
 fibmap_f2fs_SOURCES = fibmap.c
 parse_f2fs_SOURCES = f2fs_io_parse.c
+f2fscrypt_SOURCES = f2fscrypt.c sha512.c
+f2fscrypt_LDFLAGS = -luuid
+dist_man_MANS = f2fscrypt.8
diff --git a/tools/f2fscrypt.8 b/tools/f2fscrypt.8
new file mode 100644
index 0000000..a60adc8
--- /dev/null
+++ b/tools/f2fscrypt.8
@@ -0,0 +1,102 @@
+.TH F2FSCRYPT 8
+.SH NAME
+f2fscrypt \- f2fs filesystem encryption utility
+.SH SYNOPSIS
+.B f2fscrypt add_key -S \fR[\fB -k \fIkeyring\fR ] [\fB-v\fR] [\fB-q\fR] [ \fI path\fR ... ]
+.br
+.B f2fscrypt new_session
+.br
+.B f2fscrypt get_policy \fIpath\fR ...
+.br
+.B f2fscrypt set_policy \fIpolicy path\fR ...
+.SH DESCRIPTION
+.B f2fscrypt
+performs encryption management for f2fs file systems.
+.SH COMMANDS
+.TP
+.B f2fscrypt add_key -S \fR[\fB -k \fIkeyring\fR ] [\fB-v\fR] [\fB-q\fR] [ \fI path\fR ... ]
+Prompts the user for a passphrase and inserts it into the specified
+keyring.  If no keyring is specified, f2fscrypt will use the session
+keyring if it exists or the user session keyring if it does not.
+.IP
+If one or more directory paths are specified, f2fscrypt will try to
+set the policy of those directories to use the key just entered by
+the user.
+.TP
+.B f2fscrypt get_policy \fIpath\fR ...
+Print the policy for the directories specified on the command line.
+.TP
+.B f2fscrypt new_session
+Give the invoking process (typically a shell) a new session keyring,
+discarding its old session keyring.
+.TP
+.B f2fscrypt set_policy \fIpolicy path\fR ...
+Sets the policy for the directories specified on the command line.
+All directories must be empty to set the policy; if the directory
+already has a policy established, f2fscrypt will validate that the
+policy matches what was specified.  A policy is an encryption key
+identifier consisting of 16 hexadecimal characters.
+.SH NOTES
+The target directory must be empty.
+.SH EXAMPLE
+.nf
+Formats a f2fs filesytem that supports encrypt.
+
+.ft R
+# mkfs.f2fs -O encrypt /dev/sdxx
+# mount /dev/sdxx /encrypted/
+# mkdir /encrypted/dir
+
+.nf
+First create the key in the keyring use an simple salt
+(or generate a random salt).
+Then use it to set the policy for the directory to be encrypted.
+
+.ft R
+# f2fscrypt add_key -S 0x1234
+  Enter passphrase (echo disabled):
+  Added key with descriptor [28e21cc0c4393da1]
+
+# f2fscrypt set_policy 28e21cc0c4393da1 /encrypted/dir
+  Key with descriptor [28e21cc0c4393da1] applied to /encrypted/dir.
+
+# touch /encrypted/dir/test.txt
+# ls -l /encrypted/dir/
+  -rw-r--r--. 1 root root 0 Mar  5 21:41 test.txt
+
+.nf
+After each reboot, the same command can be used set the key for
+decryption of the directory and its descendants.
+
+.ft R
+# ls -l /encrypted/dir/
+  -rw-r--r--. 1 root root 0 Mar  5 21:41 zbx7tsUEMLzh+AUVMkQcnB
+
+# f2fscrypt get_policy /encrypted/dir/
+  /encrypted/dir/: 28e21cc0c4393da1
+
+# f2fscrypt add_key -S 0x1234
+  Enter passphrase (echo disabled):
+  Added key with descriptor [28e21cc0c4393da1]
+
+# ls -l /encrypted/dir/
+  -rw-r--r--. 1 root root 0 Mar  5 21:41 test.txt
+
+.nf
+Show process keyrings.
+
+.ft R
+# keyctl show
+  Session Keyring
+    84022412 --alswrv      0     0  keyring: _ses
+   204615789 --alswrv      0 65534   \\_ keyring: _uid.0
+   529474961 --alsw-v      0     0   \\_ logon: f2fs:28e21cc0c4393da1
+
+.SH AUTHOR
+Written by Kinglong Mee <kinglongmee@gmail.com>,
+Migrated from e4crypt that Written by Michael Halcrow <mhalcrow@google.com>,
+Ildar Muslukhov <muslukhovi@gmail.com>, and Theodore Ts'o <tytso@mit.edu>
+.SH SEE ALSO
+.BR keyctl (1),
+.BR mkfs.f2fs (8),
+.BR mount (8).
diff --git a/tools/f2fscrypt.c b/tools/f2fscrypt.c
new file mode 100644
index 0000000..48ea5f6
--- /dev/null
+++ b/tools/f2fscrypt.c
@@ -0,0 +1,916 @@
+/*
+ * f2fscrypt.c - f2fs encryption management utility
+ *
+ * Authors: Kinglong Mee <kinglongmee@gmail.com>
+ *
+ * Copied from e4crypt that for ext4 filesystem.
+ * Authors: Michael Halcrow <mhalcrow@google.com>,
+ *	    Ildar Muslukhov <ildarm@google.com>
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "config.h"
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <linux/fs.h>
+#include <uuid/uuid.h>
+
+#if !defined(HAVE_ADD_KEY) || !defined(HAVE_KEYCTL)
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SYS_KEY_H
+#include <sys/key.h>
+#endif
+
+#define F2FS_MAX_KEY_SIZE		64
+#define F2FS_MAX_PASSPHRASE_SIZE	1024
+#define F2FS_MAX_SALT_SIZE		256
+
+/* Encryption algorithms, key size and key reference len */
+#define F2FS_ENCRYPTION_MODE_INVALID		0
+#define F2FS_ENCRYPTION_MODE_AES_256_XTS	1
+#define F2FS_ENCRYPTION_MODE_AES_256_GCM	2
+#define F2FS_ENCRYPTION_MODE_AES_256_CBC	3
+#define F2FS_ENCRYPTION_MODE_AES_256_CTS	4
+
+#define F2FS_AES_256_XTS_KEY_SIZE		64
+#define F2FS_AES_256_GCM_KEY_SIZE		32
+#define F2FS_AES_256_CBC_KEY_SIZE		32
+#define F2FS_AES_256_CTS_KEY_SIZE		32
+#define F2FS_MAX_KEY_SIZE			64
+
+/* Password derivation constants */
+#define F2FS_MAX_PASSPHRASE_SIZE		1024
+#define F2FS_MAX_SALT_SIZE			256
+#define F2FS_PBKDF2_ITERATIONS			0xFFFF
+
+/* special process keyring shortcut IDs */
+#define KEY_SPEC_THREAD_KEYRING		-1
+#define KEY_SPEC_PROCESS_KEYRING	-2
+#define KEY_SPEC_SESSION_KEYRING	-3
+#define KEY_SPEC_USER_KEYRING		-4
+#define KEY_SPEC_USER_SESSION_KEYRING	-5
+#define KEY_SPEC_GROUP_KEYRING		-6
+
+#define KEYCTL_GET_KEYRING_ID		0
+#define KEYCTL_JOIN_SESSION_KEYRING	1
+#define KEYCTL_DESCRIBE			6
+#define KEYCTL_SEARCH			10
+#define KEYCTL_SESSION_TO_PARENT	18
+
+/*
+ * File system encryption support
+ */
+/* Policy provided via an ioctl on the topmost directory */
+#define F2FS_KEY_DESCRIPTOR_SIZE	8
+#define F2FS_KEY_REF_STR_BUF_SIZE ((F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1)
+
+struct f2fs_fscrypt_policy {
+	__u8 version;
+	__u8 contents_encryption_mode;
+	__u8 filenames_encryption_mode;
+	__u8 flags;
+	__u8 master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
+} __attribute__((packed));
+
+#define F2FS_IOC_SET_ENCRYPTION_POLICY	_IOR('f', 19, struct f2fs_fscrypt_policy)
+#define F2FS_IOC_GET_ENCRYPTION_PWSALT	_IOW('f', 20, __u8[16])
+#define F2FS_IOC_GET_ENCRYPTION_POLICY	_IOW('f', 21, struct f2fs_fscrypt_policy)
+
+typedef int32_t key_serial_t;
+
+
+
+#define OPT_VERBOSE	0x0001
+#define OPT_QUIET	0x0002
+
+struct f2fs_encryption_key {
+        __u32 mode;
+        char raw[F2FS_MAX_KEY_SIZE];
+        __u32 size;
+} __attribute__((__packed__));
+
+int options;
+
+extern void f2fs_sha512(const unsigned char *in, unsigned long in_size,
+						unsigned char *out);
+
+#ifndef HAVE_KEYCTL
+static long keyctl(int cmd, ...)
+{
+	va_list va;
+	unsigned long arg2, arg3, arg4, arg5;
+
+	va_start(va, cmd);
+	arg2 = va_arg(va, unsigned long);
+	arg3 = va_arg(va, unsigned long);
+	arg4 = va_arg(va, unsigned long);
+	arg5 = va_arg(va, unsigned long);
+	va_end(va);
+	return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+}
+#endif
+
+#ifndef HAVE_ADD_KEY
+static key_serial_t add_key(const char *type, const char *description,
+			    const void *payload, size_t plen,
+			    key_serial_t keyring)
+{
+	return syscall(__NR_add_key, type, description, payload,
+		       plen, keyring);
+}
+#endif
+
+static const unsigned char *hexchars = (const unsigned char *) "0123456789abcdef";
+static const size_t hexchars_size = 16;
+
+#define SHA512_LENGTH 64
+#define F2FS_KEY_TYPE_LOGON "logon"
+#define F2FS_KEY_DESC_PREFIX "f2fs:"
+#define F2FS_KEY_DESC_PREFIX_SIZE 5
+
+static int int_log2(int arg)
+{
+	int     l = 0;
+
+	arg >>= 1;
+	while (arg) {
+		l++;
+		arg >>= 1;
+	}
+	return l;
+}
+
+static void validate_paths(int argc, char *argv[], int path_start_index)
+{
+	int x;
+	int valid = 1;
+	struct stat st;
+
+	for (x = path_start_index; x < argc; x++) {
+		int ret = access(argv[x], W_OK);
+		if (ret) {
+		invalid:
+			perror(argv[x]);
+			valid = 0;
+			continue;
+		}
+		ret = stat(argv[x], &st);
+		if (ret < 0)
+			goto invalid;
+		if (!S_ISDIR(st.st_mode)) {
+			fprintf(stderr, "%s is not a directory\n", argv[x]);
+			goto invalid;
+		}
+	}
+	if (!valid)
+		exit(1);
+}
+
+static int hex2byte(const char *hex, size_t hex_size, unsigned char *bytes,
+		    size_t bytes_size)
+{
+	size_t x;
+	unsigned char *h, *l;
+
+	if (hex_size % 2)
+		return -EINVAL;
+	for (x = 0; x < hex_size; x += 2) {
+		h = memchr(hexchars, hex[x], hexchars_size);
+		if (!h)
+			return -EINVAL;
+		l = memchr(hexchars, hex[x + 1], hexchars_size);
+		if (!l)
+			return -EINVAL;
+		if ((x >> 1) >= bytes_size)
+			return -EINVAL;
+		bytes[x >> 1] = (((unsigned char)(h - hexchars) << 4) +
+				 (unsigned char)(l - hexchars));
+	}
+	return 0;
+}
+
+/*
+ * Salt handling
+ */
+struct salt {
+	unsigned char *salt;
+	char key_ref_str[F2FS_KEY_REF_STR_BUF_SIZE];
+	unsigned char key_desc[F2FS_KEY_DESCRIPTOR_SIZE];
+	unsigned char key[F2FS_MAX_KEY_SIZE];
+	size_t salt_len;
+};
+struct salt *salt_list;
+unsigned num_salt;
+unsigned max_salt;
+char in_passphrase[F2FS_MAX_PASSPHRASE_SIZE];
+
+static struct salt *find_by_salt(unsigned char *salt, size_t salt_len)
+{
+	unsigned int i;
+	struct salt *p;
+
+	for (i = 0, p = salt_list; i < num_salt; i++, p++)
+		if ((p->salt_len == salt_len) &&
+		    !memcmp(p->salt, salt, salt_len))
+			return p;
+	return NULL;
+}
+
+static void add_salt(unsigned char *salt, size_t salt_len)
+{
+	if (find_by_salt(salt, salt_len))
+		return;
+	if (num_salt >= max_salt) {
+		max_salt = num_salt + 10;
+		salt_list = realloc(salt_list, max_salt * sizeof(struct salt));
+		if (!salt_list) {
+			fprintf(stderr, "Couldn't allocate salt list\n");
+			exit(1);
+		}
+	}
+	salt_list[num_salt].salt = salt;
+	salt_list[num_salt].salt_len = salt_len;
+	num_salt++;
+}
+
+static void clear_secrets(void)
+{
+	if (salt_list) {
+		memset(salt_list, 0, sizeof(struct salt) * max_salt);
+		free(salt_list);
+		salt_list = NULL;
+	}
+	memset(in_passphrase, 0, sizeof(in_passphrase));
+}
+
+static void die_signal_handler(int signum, siginfo_t *siginfo, void *context)
+{
+	clear_secrets();
+	exit(-1);
+}
+
+static void sigcatcher_setup(void)
+{
+	struct sigaction	sa;
+
+	memset(&sa, 0, sizeof(struct sigaction));
+	sa.sa_sigaction = die_signal_handler;
+	sa.sa_flags = SA_SIGINFO;
+
+	sigaction(SIGHUP, &sa, 0);
+	sigaction(SIGINT, &sa, 0);
+	sigaction(SIGQUIT, &sa, 0);
+	sigaction(SIGFPE, &sa, 0);
+	sigaction(SIGILL, &sa, 0);
+	sigaction(SIGBUS, &sa, 0);
+	sigaction(SIGSEGV, &sa, 0);
+	sigaction(SIGABRT, &sa, 0);
+	sigaction(SIGPIPE, &sa, 0);
+	sigaction(SIGALRM, &sa, 0);
+	sigaction(SIGTERM, &sa, 0);
+	sigaction(SIGUSR1, &sa, 0);
+	sigaction(SIGUSR2, &sa, 0);
+	sigaction(SIGPOLL, &sa, 0);
+	sigaction(SIGPROF, &sa, 0);
+	sigaction(SIGSYS, &sa, 0);
+	sigaction(SIGTRAP, &sa, 0);
+	sigaction(SIGVTALRM, &sa, 0);
+	sigaction(SIGXCPU, &sa, 0);
+	sigaction(SIGXFSZ, &sa, 0);
+}
+
+
+#define PARSE_FLAGS_NOTSUPP_OK	0x0001
+#define PARSE_FLAGS_FORCE_FN	0x0002
+
+static void parse_salt(char *salt_str, int flags)
+{
+	unsigned char buf[F2FS_MAX_SALT_SIZE];
+	char *cp = salt_str;
+	unsigned char *salt_buf;
+	int fd, ret, salt_len = 0;
+
+	if (flags & PARSE_FLAGS_FORCE_FN)
+		goto salt_from_filename;
+	if (strncmp(cp, "s:", 2) == 0) {
+		cp += 2;
+		salt_len = strlen(cp);
+		if (salt_len >= F2FS_MAX_SALT_SIZE)
+			goto invalid_salt;
+		strncpy((char *) buf, cp, sizeof(buf));
+	} else if (cp[0] == '/') {
+	salt_from_filename:
+		fd = open(cp, O_RDONLY | O_DIRECTORY);
+		if (fd == -1 && errno == ENOTDIR)
+			fd = open(cp, O_RDONLY);
+		if (fd == -1) {
+			perror(cp);
+			exit(1);
+		}
+		ret = ioctl(fd, F2FS_IOC_GET_ENCRYPTION_PWSALT, &buf);
+		close(fd);
+		if (ret < 0) {
+			if (flags & PARSE_FLAGS_NOTSUPP_OK)
+				return;
+			perror("F2FS_IOC_GET_ENCRYPTION_PWSALT");
+			exit(1);
+		}
+		if (options & OPT_VERBOSE) {
+			char tmp[80];
+			uuid_unparse(buf, tmp);
+			printf("%s has pw salt %s\n", cp, tmp);
+		}
+		salt_len = 16;
+	} else if (strncmp(cp, "f:", 2) == 0) {
+		cp += 2;
+		goto salt_from_filename;
+	} else if (strncmp(cp, "0x", 2) == 0) {
+		unsigned char *h, *l;
+
+		cp += 2;
+		if (strlen(cp) & 1)
+			goto invalid_salt;
+		while (*cp) {
+			if (salt_len >= F2FS_MAX_SALT_SIZE)
+				goto invalid_salt;
+			h = memchr(hexchars, *cp++, hexchars_size);
+			l = memchr(hexchars, *cp++, hexchars_size);
+			if (!h || !l)
+				goto invalid_salt;
+			buf[salt_len++] =
+				(((unsigned char)(h - hexchars) << 4) +
+				 (unsigned char)(l - hexchars));
+		}
+	} else if (uuid_parse(cp, buf) == 0) {
+		salt_len = 16;
+	} else {
+	invalid_salt:
+		fprintf(stderr, "Invalid salt: %s\n", salt_str);
+		exit(1);
+	}
+	salt_buf = malloc(salt_len);
+	if (!salt_buf) {
+		fprintf(stderr, "Couldn't allocate salt\n");
+		exit(1);
+	}
+	memcpy(salt_buf, buf, salt_len);
+	add_salt(salt_buf, salt_len);
+}
+
+static void set_policy(struct salt *set_salt, int pad,
+		       int argc, char *argv[], int path_start_index)
+{
+	struct salt *salt;
+	struct f2fs_fscrypt_policy policy;
+	uuid_t	uu;
+	int fd;
+	int x;
+	int rc;
+
+	if ((pad != 4) && (pad != 8) &&
+		 (pad != 16) && (pad != 32)) {
+		fprintf(stderr, "Invalid padding %d\n", pad);
+		exit(1);
+	}
+
+	for (x = path_start_index; x < argc; x++) {
+		fd = open(argv[x], O_DIRECTORY);
+		if (fd == -1) {
+			perror(argv[x]);
+			exit(1);
+		}
+		if (set_salt)
+			salt = set_salt;
+		else {
+			if (ioctl(fd, F2FS_IOC_GET_ENCRYPTION_PWSALT,
+				  &uu) < 0) {
+				perror("F2FS_IOC_GET_ENCRYPTION_PWSALT");
+				exit(1);
+			}
+			salt = find_by_salt(uu, sizeof(uu));
+			if (!salt) {
+				fprintf(stderr, "Couldn't find salt!?!\n");
+				exit(1);
+			}
+		}
+		policy.version = 0;
+		policy.contents_encryption_mode =
+			F2FS_ENCRYPTION_MODE_AES_256_XTS;
+		policy.filenames_encryption_mode =
+			F2FS_ENCRYPTION_MODE_AES_256_CTS;
+		policy.flags = int_log2(pad >> 2);
+		memcpy(policy.master_key_descriptor, salt->key_desc,
+		       F2FS_KEY_DESCRIPTOR_SIZE);
+		rc = ioctl(fd, F2FS_IOC_SET_ENCRYPTION_POLICY, &policy);
+		close(fd);
+		if (rc) {
+			printf("Error [%s] setting policy.\nThe key descriptor "
+			       "[%s] may not match the existing encryption "
+			       "context for directory [%s].\n",
+			       strerror(errno), salt->key_ref_str, argv[x]);
+			continue;
+		}
+		printf("Key with descriptor [%s] applied to %s.\n",
+		       salt->key_ref_str, argv[x]);
+	}
+}
+
+static void pbkdf2_sha512(const char *passphrase, struct salt *salt,
+			  unsigned int count,
+			  unsigned char derived_key[F2FS_MAX_KEY_SIZE])
+{
+	size_t passphrase_size = strlen(passphrase);
+	unsigned char buf[SHA512_LENGTH + F2FS_MAX_PASSPHRASE_SIZE] = {0};
+	unsigned char tempbuf[SHA512_LENGTH] = {0};
+	char final[SHA512_LENGTH] = {0};
+	unsigned char saltbuf[F2FS_MAX_SALT_SIZE + F2FS_MAX_PASSPHRASE_SIZE] = {0};
+	int actual_buf_len = SHA512_LENGTH + passphrase_size;
+	int actual_saltbuf_len = F2FS_MAX_SALT_SIZE + passphrase_size;
+	unsigned int x, y;
+	__u32 *final_u32 = (__u32 *)final;
+	__u32 *temp_u32 = (__u32 *)tempbuf;
+
+	if (passphrase_size > F2FS_MAX_PASSPHRASE_SIZE) {
+		printf("Passphrase size is %zd; max is %d.\n", passphrase_size,
+		       F2FS_MAX_PASSPHRASE_SIZE);
+		exit(1);
+	}
+	if (salt->salt_len > F2FS_MAX_SALT_SIZE) {
+		printf("Salt size is %zd; max is %d.\n", salt->salt_len,
+		       F2FS_MAX_SALT_SIZE);
+		exit(1);
+	}
+	assert(F2FS_MAX_KEY_SIZE <= SHA512_LENGTH);
+
+	memcpy(saltbuf, salt->salt, salt->salt_len);
+	memcpy(&saltbuf[F2FS_MAX_SALT_SIZE], passphrase, passphrase_size);
+
+	memcpy(&buf[SHA512_LENGTH], passphrase, passphrase_size);
+
+	for (x = 0; x < count; ++x) {
+		if (x == 0) {
+			f2fs_sha512(saltbuf, actual_saltbuf_len, tempbuf);
+		} else {
+			/*
+			 * buf: [previous hash || passphrase]
+			 */
+			memcpy(buf, tempbuf, SHA512_LENGTH);
+			f2fs_sha512(buf, actual_buf_len, tempbuf);
+		}
+		for (y = 0; y < (sizeof(final) / sizeof(*final_u32)); ++y)
+			final_u32[y] = final_u32[y] ^ temp_u32[y];
+	}
+	memcpy(derived_key, final, F2FS_MAX_KEY_SIZE);
+}
+
+static int disable_echo(struct termios *saved_settings)
+{
+	struct termios current_settings;
+	int rc = 0;
+
+	rc = tcgetattr(0, &current_settings);
+	if (rc)
+		return rc;
+	*saved_settings = current_settings;
+	current_settings.c_lflag &= ~ECHO;
+	rc = tcsetattr(0, TCSANOW, &current_settings);
+
+	return rc;
+}
+
+static void get_passphrase(char *passphrase, int len)
+{
+	char *p;
+	struct termios current_settings;
+
+	assert(len > 0);
+	disable_echo(&current_settings);
+	p = fgets(passphrase, len, stdin);
+	tcsetattr(0, TCSANOW, &current_settings);
+	printf("\n");
+	if (!p) {
+		printf("Aborting.\n");
+		exit(1);
+	}
+	p = strrchr(passphrase, '\n');
+	if (!p)
+		p = passphrase + len - 1;
+	*p = '\0';
+}
+
+struct keyring_map {
+	char name[4];
+	size_t name_len;
+	int code;
+};
+
+static const struct keyring_map keyrings[] = {
+	{"@us", 3, KEY_SPEC_USER_SESSION_KEYRING},
+	{"@u", 2, KEY_SPEC_USER_KEYRING},
+	{"@s", 2, KEY_SPEC_SESSION_KEYRING},
+	{"@g", 2, KEY_SPEC_GROUP_KEYRING},
+	{"@p", 2, KEY_SPEC_PROCESS_KEYRING},
+	{"@t", 2, KEY_SPEC_THREAD_KEYRING},
+};
+
+static int get_keyring_id(const char *keyring)
+{
+	unsigned int x;
+	char *end;
+
+	/*
+	 * If no keyring is specified, by default use either the user
+	 * session key ring or the session keyring.  Fetching the
+	 * session keyring will return the user session keyring if no
+	 * session keyring has been set.
+	 *
+	 * We need to do this instead of simply adding the key to
+	 * KEY_SPEC_SESSION_KEYRING since trying to add a key to a
+	 * session keyring that does not yet exist will cause the
+	 * kernel to create a session keyring --- which wil then get
+	 * garbage collected as soon as f2fscrypt exits.
+	 *
+	 * The fact that the keyctl system call and the add_key system
+	 * call treats KEY_SPEC_SESSION_KEYRING differently when a
+	 * session keyring does not exist is very unfortunate and
+	 * confusing, but so it goes...
+	 */
+	if (keyring == NULL)
+		return keyctl(KEYCTL_GET_KEYRING_ID,
+			      KEY_SPEC_SESSION_KEYRING, 0);
+	for (x = 0; x < (sizeof(keyrings) / sizeof(keyrings[0])); ++x) {
+		if (strcmp(keyring, keyrings[x].name) == 0) {
+			return keyrings[x].code;
+		}
+	}
+	x = strtoul(keyring, &end, 10);
+	if (*end == '\0') {
+		if (keyctl(KEYCTL_DESCRIBE, x, NULL, 0) < 0)
+			return 0;
+		return x;
+	}
+	return 0;
+}
+
+static void generate_key_ref_str(struct salt *salt)
+{
+	unsigned char key_ref1[SHA512_LENGTH];
+	unsigned char key_ref2[SHA512_LENGTH];
+	int x;
+
+	f2fs_sha512(salt->key, F2FS_MAX_KEY_SIZE, key_ref1);
+	f2fs_sha512(key_ref1, SHA512_LENGTH, key_ref2);
+	memcpy(salt->key_desc, key_ref2, F2FS_KEY_DESCRIPTOR_SIZE);
+	for (x = 0; x < F2FS_KEY_DESCRIPTOR_SIZE; ++x) {
+		sprintf(&salt->key_ref_str[x * 2], "%02x",
+			salt->key_desc[x]);
+	}
+	salt->key_ref_str[F2FS_KEY_REF_STR_BUF_SIZE - 1] = '\0';
+}
+
+static void insert_key_into_keyring(const char *keyring, struct salt *salt)
+{
+	int keyring_id = get_keyring_id(keyring);
+	struct f2fs_encryption_key key;
+	char key_ref_full[F2FS_KEY_DESC_PREFIX_SIZE +
+			  F2FS_KEY_REF_STR_BUF_SIZE];
+	int rc;
+
+	if (keyring_id == 0) {
+		printf("Invalid keyring [%s].\n", keyring);
+		exit(1);
+	}
+	sprintf(key_ref_full, "%s%s", F2FS_KEY_DESC_PREFIX,
+		salt->key_ref_str);
+	rc = keyctl(KEYCTL_SEARCH, keyring_id, F2FS_KEY_TYPE_LOGON,
+		    key_ref_full, 0);
+	if (rc != -1) {
+		if ((options & OPT_QUIET) == 0)
+			printf("Key with descriptor [%s] already exists\n",
+			       salt->key_ref_str);
+		return;
+	} else if ((rc == -1) && (errno != ENOKEY)) {
+		printf("keyctl_search failed: %s\n", strerror(errno));
+		if (errno == -EINVAL)
+			printf("Keyring [%s] is not available.\n", keyring);
+		exit(1);
+	}
+	key.mode = F2FS_ENCRYPTION_MODE_AES_256_XTS;
+	memcpy(key.raw, salt->key, F2FS_MAX_KEY_SIZE);
+	key.size = F2FS_MAX_KEY_SIZE;
+	rc = add_key(F2FS_KEY_TYPE_LOGON, key_ref_full, (void *)&key,
+		     sizeof(key), keyring_id);
+	if (rc == -1) {
+		if (errno == EDQUOT) {
+			printf("Error adding key to keyring; quota exceeded\n");
+		} else {
+			printf("Error adding key with key descriptor [%s]: "
+			       "%s\n", salt->key_ref_str, strerror(errno));
+		}
+		exit(1);
+	} else {
+		if ((options & OPT_QUIET) == 0)
+			printf("Added key with descriptor [%s]\n",
+			       salt->key_ref_str);
+	}
+}
+
+static void get_default_salts(void)
+{
+	FILE	*f = setmntent("/etc/mtab", "r");
+	struct mntent *mnt;
+
+	while (f && ((mnt = getmntent(f)) != NULL)) {
+		if (strcmp(mnt->mnt_type, "f2fs") ||
+		    access(mnt->mnt_dir, R_OK))
+			continue;
+		parse_salt(mnt->mnt_dir, PARSE_FLAGS_NOTSUPP_OK);
+	}
+	endmntent(f);
+}
+
+/* Functions which implement user commands */
+
+struct cmd_desc {
+	const char *cmd_name;
+	void (*cmd_func)(int, char **, const struct cmd_desc *);
+	const char *cmd_desc;
+	const char *cmd_help;
+	int cmd_flags;
+};
+
+#define CMD_HIDDEN 	0x0001
+
+static void do_help(int argc, char **argv, const struct cmd_desc *cmd);
+
+#define add_key_desc "adds a key to the user's keyring"
+#define add_key_help \
+"f2fscrypt add_key -S salt [ -k keyring ] [-v] [-q] [ path ... ]\n\n" \
+"Prompts the user for a passphrase and inserts it into the specified\n" \
+"keyring.  If no keyring is specified, f2fscrypt will use the session\n" \
+"keyring if it exists or the user session keyring if it does not.\n\n" \
+"If one or more directory paths are specified, f2fscrypt will try to\n" \
+"set the policy of those directories to use the key just entered by\n" \
+"the user.\n"
+
+static void do_add_key(int argc, char **argv, const struct cmd_desc *cmd)
+{
+	struct salt *salt;
+	char *keyring = NULL;
+	int i, opt, pad = 4;
+	unsigned j;
+
+	while ((opt = getopt(argc, argv, "k:S:p:vq")) != -1) {
+		switch (opt) {
+		case 'k':
+			/* Specify a keyring. */
+			keyring = optarg;
+			break;
+		case 'p':
+			pad = atoi(optarg);
+			break;
+		case 'S':
+			/* Salt value for passphrase. */
+			parse_salt(optarg, 0);
+			break;
+		case 'v':
+			options |= OPT_VERBOSE;
+			break;
+		case 'q':
+			options |= OPT_QUIET;
+			break;
+		default:
+			fprintf(stderr, "Unrecognized option: %c\n", opt);
+		case '?':
+			fputs("USAGE:\n  ", stderr);
+			fputs(cmd->cmd_help, stderr);
+			exit(1);
+		}
+	}
+	if (num_salt == 0)
+		get_default_salts();
+	if (num_salt == 0) {
+		fprintf(stderr, "No salt values available\n");
+		exit(1);
+	}
+	validate_paths(argc, argv, optind);
+	for (i = optind; i < argc; i++)
+		parse_salt(argv[i], PARSE_FLAGS_FORCE_FN);
+	printf("Enter passphrase (echo disabled): ");
+	get_passphrase(in_passphrase, sizeof(in_passphrase));
+	for (j = 0, salt = salt_list; j < num_salt; j++, salt++) {
+		pbkdf2_sha512(in_passphrase, salt,
+			      F2FS_PBKDF2_ITERATIONS, salt->key);
+		generate_key_ref_str(salt);
+		insert_key_into_keyring(keyring, salt);
+	}
+	if (optind != argc)
+		set_policy(NULL, pad, argc, argv, optind);
+	clear_secrets();
+	exit(0);
+}
+
+#define set_policy_desc "sets a policy for directories"
+#define set_policy_help \
+"f2fscrypt set_policy policy path ... \n\n" \
+"Sets the policy for the directories specified on the command line.\n" \
+"All directories must be empty to set the policy; if the directory\n" \
+"already has a policy established, f2fscrypt will validate that it the\n" \
+"policy matches what was specified.  A policy is an encryption key\n" \
+"identifier consisting of 16 hexadecimal characters.\n"
+
+static void do_set_policy(int argc, char **argv, const struct cmd_desc *cmd)
+{
+	struct salt saltbuf;
+	int c, pad = 4;
+
+	while ((c = getopt (argc, argv, "p:")) != EOF) {
+		switch (c) {
+		case 'p':
+			pad = atoi(optarg);
+			break;
+		}
+	}
+
+	if (argc < optind + 2) {
+		fprintf(stderr, "Missing required argument(s).\n\n");
+		fputs("USAGE:\n  ", stderr);
+		fputs(cmd->cmd_help, stderr);
+		exit(1);
+	}
+
+	if ((strlen(argv[optind]) != (F2FS_KEY_DESCRIPTOR_SIZE * 2)) ||
+	    hex2byte(argv[optind], (F2FS_KEY_DESCRIPTOR_SIZE * 2),
+		     saltbuf.key_desc, F2FS_KEY_DESCRIPTOR_SIZE)) {
+		printf("Invalid key descriptor [%s]. Valid characters "
+		       "are 0-9 and a-f, lower case.  "
+		       "Length must be %d.\n",
+		       argv[optind], (F2FS_KEY_DESCRIPTOR_SIZE * 2));
+			exit(1);
+	}
+	validate_paths(argc, argv, optind+1);
+	strcpy(saltbuf.key_ref_str, argv[optind]);
+	set_policy(&saltbuf, pad, argc, argv, optind+1);
+	exit(0);
+}
+
+#define get_policy_desc "get the encryption for directories"
+#define get_policy_help \
+"f2fscrypt get_policy path ... \n\n" \
+"Gets the policy for the directories specified on the command line.\n"
+
+static void do_get_policy(int argc, char **argv, const struct cmd_desc *cmd)
+{
+	struct f2fs_fscrypt_policy policy;
+	struct stat st;
+	int i, j, fd, rc;
+
+	if (argc < 2) {
+		fprintf(stderr, "Missing required argument(s).\n\n");
+		fputs("USAGE:\n  ", stderr);
+		fputs(cmd->cmd_help, stderr);
+		exit(1);
+	}
+
+	for (i = 1; i < argc; i++) {
+		if (stat(argv[i], &st) < 0) {
+			perror(argv[i]);
+			continue;
+		}
+		fd = open(argv[i],
+			  S_ISDIR(st.st_mode) ? O_DIRECTORY : O_RDONLY);
+		if (fd == -1) {
+			perror(argv[i]);
+			exit(1);
+		}
+		rc = ioctl(fd, F2FS_IOC_GET_ENCRYPTION_POLICY, &policy);
+		close(fd);
+		if (rc) {
+			printf("Error getting policy for %s: %s\n",
+			       argv[i], strerror(errno));
+			continue;
+		}
+		printf("%s: ", argv[i]);
+		for (j = 0; j < F2FS_KEY_DESCRIPTOR_SIZE; j++) {
+			printf("%02x", (unsigned char) policy.master_key_descriptor[j]);
+		}
+		fputc('\n', stdout);
+	}
+	exit(0);
+}
+
+#define new_session_desc "give the invoking process a new session keyring"
+#define new_session_help \
+"f2fscrypt new_session\n\n" \
+"Give the invoking process (typically a shell) a new session keyring,\n" \
+"discarding its old session keyring.\n"
+
+static void do_new_session(int argc, char **argv, const struct cmd_desc *cmd)
+{
+	long keyid, ret;
+
+	if (argc > 1) {
+		fputs("Excess arguments\n\n", stderr);
+		fputs(cmd->cmd_help, stderr);
+		exit(1);
+	}
+	keyid = keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL);
+	if (keyid < 0) {
+		perror("KEYCTL_JOIN_SESSION_KEYRING");
+		exit(1);
+	}
+	ret = keyctl(KEYCTL_SESSION_TO_PARENT, NULL);
+	if (ret < 0) {
+		perror("KEYCTL_SESSION_TO_PARENT");
+		exit(1);
+	}
+	printf("Switched invoking process to new session keyring %ld\n", keyid);
+	exit(0);
+}
+
+#define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
+#define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
+
+const struct cmd_desc cmd_list[] = {
+	_CMD(help),
+	CMD(add_key),
+	CMD(get_policy),
+	CMD(new_session),
+	CMD(set_policy),
+	{ NULL, NULL, NULL, NULL, 0 }
+};
+
+static void do_help(int argc, char **argv, const struct cmd_desc *cmd)
+{
+	const struct cmd_desc *p;
+
+	if (argc > 1) {
+		for (p = cmd_list; p->cmd_name; p++) {
+			if (p->cmd_flags & CMD_HIDDEN)
+				continue;
+			if (strcmp(p->cmd_name, argv[1]) == 0) {
+				putc('\n', stdout);
+				fputs("USAGE:\n  ", stdout);
+				fputs(p->cmd_help, stdout);
+				exit(0);
+			}
+		}
+		printf("Unknown command: %s\n\n", argv[1]);
+	}
+
+	fputs("Available commands:\n", stdout);
+	for (p = cmd_list; p->cmd_name; p++) {
+		if (p->cmd_flags & CMD_HIDDEN)
+			continue;
+		printf("  %-20s %s\n", p->cmd_name, p->cmd_desc);
+	}
+	printf("\nTo get more information on a command, "
+	       "type 'f2fscrypt help cmd'\n");
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	const struct cmd_desc *cmd;
+
+	if (argc < 2)
+		do_help(argc, argv, cmd_list);
+
+	sigcatcher_setup();
+	for (cmd = cmd_list; cmd->cmd_name; cmd++) {
+		if (strcmp(cmd->cmd_name, argv[1]) == 0) {
+			cmd->cmd_func(argc-1, argv+1, cmd);
+			exit(0);
+		}
+	}
+	printf("Unknown command: %s\n\n", argv[1]);
+	do_help(1, argv, cmd_list);
+	return 0;
+}
diff --git a/tools/sha512.c b/tools/sha512.c
new file mode 100644
index 0000000..bf0d9a4
--- /dev/null
+++ b/tools/sha512.c
@@ -0,0 +1,323 @@
+/*
+ * sha512.c --- The sha512 algorithm
+ *
+ * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+ * (copied from libtomcrypt and then relicensed under GPLv2)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+
+#include "config.h"
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <linux/fs.h>
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#define F2FS_SHA512_LENGTH 64
+
+/* the K array */
+#define CONST64(n) n
+static const __u64 K[80] = {
+	CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+	CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+	CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+	CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+	CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+	CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+	CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+	CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+	CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+	CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+	CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+	CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+	CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+	CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+	CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+	CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+	CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+	CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+	CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+	CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+	CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+	CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+	CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+	CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+	CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+	CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+	CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+	CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+	CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+	CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+	CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+	CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+	CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+	CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+	CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+	CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+	CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+	CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+	CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+	CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
+#define S(x, n)         ROR64c(x, n)
+#define R(x, n)         (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)n))
+#define Sigma0(x)       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x)       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x)       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x)       (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#define RND(a,b,c,d,e,f,g,h,i)\
+		t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];\
+		t1 = Sigma0(a) + Maj(a, b, c);\
+		d += t0;\
+		h  = t0 + t1;
+#define STORE64H(x, y) \
+	do { \
+		(y)[0] = (unsigned char)(((x)>>56)&255);\
+		(y)[1] = (unsigned char)(((x)>>48)&255);\
+		(y)[2] = (unsigned char)(((x)>>40)&255);\
+		(y)[3] = (unsigned char)(((x)>>32)&255);\
+		(y)[4] = (unsigned char)(((x)>>24)&255);\
+		(y)[5] = (unsigned char)(((x)>>16)&255);\
+		(y)[6] = (unsigned char)(((x)>>8)&255);\
+		(y)[7] = (unsigned char)((x)&255); } while(0)
+
+#define LOAD64H(x, y)\
+	do {x = \
+		(((__u64)((y)[0] & 255)) << 56) |\
+		(((__u64)((y)[1] & 255)) << 48) |\
+		(((__u64)((y)[2] & 255)) << 40) |\
+		(((__u64)((y)[3] & 255)) << 32) |\
+		(((__u64)((y)[4] & 255)) << 24) |\
+		(((__u64)((y)[5] & 255)) << 16) |\
+		(((__u64)((y)[6] & 255)) << 8) |\
+		(((__u64)((y)[7] & 255)));\
+	} while(0)
+
+#define ROR64c(x, y) \
+    ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)(y)&CONST64(63))) | \
+      ((x)<<((__u64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+struct sha512_state {
+	__u64  length, state[8];
+	unsigned long curlen;
+	unsigned char buf[128];
+};
+
+/* This is a highly simplified version from libtomcrypt */
+struct hash_state {
+	struct sha512_state sha512;
+};
+
+static void sha512_compress(struct hash_state * md, const unsigned char *buf)
+{
+	__u64 S[8], W[80], t0, t1;
+	int i;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++) {
+		S[i] = md->sha512.state[i];
+	}
+
+	/* copy the state into 1024-bits into W[0..15] */
+	for (i = 0; i < 16; i++) {
+		LOAD64H(W[i], buf + (8*i));
+	}
+
+	/* fill W[16..79] */
+	for (i = 16; i < 80; i++) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] +
+			Gamma0(W[i - 15]) + W[i - 16];
+	}
+
+	for (i = 0; i < 80; i += 8) {
+		RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
+		RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
+		RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
+		RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
+		RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
+		RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
+		RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
+		RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
+	}
+
+	 /* feedback */
+	for (i = 0; i < 8; i++) {
+		md->sha512.state[i] = md->sha512.state[i] + S[i];
+	}
+}
+
+static void sha512_init(struct hash_state * md)
+{
+	md->sha512.curlen = 0;
+	md->sha512.length = 0;
+	md->sha512.state[0] = CONST64(0x6a09e667f3bcc908);
+	md->sha512.state[1] = CONST64(0xbb67ae8584caa73b);
+	md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b);
+	md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1);
+	md->sha512.state[4] = CONST64(0x510e527fade682d1);
+	md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f);
+	md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b);
+	md->sha512.state[7] = CONST64(0x5be0cd19137e2179);
+}
+
+static void sha512_done(struct hash_state * md, unsigned char *out)
+{
+	int i;
+
+	/* increase the length of the message */
+	md->sha512.length += md->sha512.curlen * CONST64(8);
+
+	/* append the '1' bit */
+	md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80;
+
+	/* if the length is currently above 112 bytes we append zeros then
+	 * compress. Then we can fall back to padding zeros and length encoding
+	 * like normal. */
+	if (md->sha512.curlen > 112) {
+		while (md->sha512.curlen < 128) {
+			md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+		}
+		sha512_compress(md, md->sha512.buf);
+		md->sha512.curlen = 0;
+	}
+
+	/* pad upto 120 bytes of zeroes note: that from 112 to 120 is the 64 MSB
+	 * of the length. We assume that you won't hash > 2^64 bits of data. */
+	while (md->sha512.curlen < 120) {
+		md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+	}
+
+	/* store length */
+	STORE64H(md->sha512.length, md->sha512.buf + 120);
+	sha512_compress(md, md->sha512.buf);
+
+	/* copy output */
+	for (i = 0; i < 8; i++) {
+		STORE64H(md->sha512.state[i], out+(8 * i));
+	}
+}
+
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#define SHA512_BLOCKSIZE 128
+static void sha512_process(struct hash_state * md,
+			   const unsigned char *in,
+			   unsigned long inlen)
+{
+	unsigned long n;
+
+	while (inlen > 0) {
+		if (md->sha512.curlen == 0 && inlen >= SHA512_BLOCKSIZE) {
+			sha512_compress(md, in);
+			md->sha512.length += SHA512_BLOCKSIZE * 8;
+			in += SHA512_BLOCKSIZE;
+			inlen -= SHA512_BLOCKSIZE;
+		} else {
+			n = MIN(inlen, (SHA512_BLOCKSIZE - md->sha512.curlen));
+			memcpy(md->sha512.buf + md->sha512.curlen,
+			       in, (size_t)n);
+			md->sha512.curlen += n;
+			in += n;
+			inlen -= n;
+			if (md->sha512.curlen == SHA512_BLOCKSIZE) {
+				sha512_compress(md, md->sha512.buf);
+				md->sha512.length += SHA512_BLOCKSIZE * 8;
+				md->sha512.curlen = 0;
+			}
+		}
+	}
+}
+
+void f2fs_sha512(const unsigned char *in, unsigned long in_size,
+		   unsigned char out[F2FS_SHA512_LENGTH])
+{
+	struct hash_state md;
+
+	sha512_init(&md);
+	sha512_process(&md, in, in_size);
+	sha512_done(&md, out);
+}
+
+#ifdef UNITTEST
+static const struct {
+	char *msg;
+	unsigned char hash[64];
+} tests[] = {
+	{ "",
+	  { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd,
+	    0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07,
+	    0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc,
+	    0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce,
+	    0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0,
+	    0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f,
+	    0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81,
+	    0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e }
+	},
+	{ "abc",
+	  { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+	    0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+	    0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+	    0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+	    0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+	    0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+	    0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+	    0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f }
+	},
+	{ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+	  { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
+	    0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
+	    0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+	    0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
+	    0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+	    0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+	    0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
+	    0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 }
+	},
+};
+
+int main(int argc, char **argv)
+{
+	int i;
+	int errors = 0;
+	unsigned char tmp[64];
+	struct hash_state md;
+
+	for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+		unsigned char *msg = (unsigned char *) tests[i].msg;
+		int len = strlen(tests[i].msg);
+
+		f2fs_sha512(msg, len, tmp);
+		printf("SHA512 test message %d: ", i);
+		if (memcmp(tmp, tests[i].hash, 64) != 0) {
+			printf("FAILED\n");
+			errors++;
+		} else
+			printf("OK\n");
+	}
+	return errors;
+}
+
+#endif /* UNITTEST */