fsck.f2fs: handle error cases

Do sanity check first and then update metadata.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
diff --git a/fsck/fsck.c b/fsck/fsck.c
index d2781d9..9ebe2be 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -80,10 +80,8 @@
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	struct hard_link_node *node = NULL, *prev = NULL;
 
-	if (fsck->hard_link_list_head == NULL) {
-		ASSERT(0);
-		return -1;
-	}
+	if (fsck->hard_link_list_head == NULL)
+		return -EINVAL;
 
 	node = fsck->hard_link_list_head;
 
@@ -92,10 +90,8 @@
 		node = node->next;
 	}
 
-	if (node == NULL || (nid != node->nid)) {
-		ASSERT(0);
-		return -1;
-	}
+	if (node == NULL || (nid != node->nid))
+		return -EINVAL;
 
 	/* Decrease link count */
 	node->links = node->links - 1;
@@ -108,9 +104,7 @@
 			prev->next = node->next;
 		free(node);
 	}
-
 	return 0;
-
 }
 
 static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid,
@@ -138,14 +132,16 @@
 					le32_to_cpu(sum_entry.nid));
 			DBG(0, "--> node block's nid      [0x%x]\n", nid);
 			ASSERT_MSG("Invalid node seg summary\n");
+			return -EINVAL;
 		}
+		return 0;
 	} else if (ret == SEG_TYPE_CUR_NODE) {
 		/* current node segment has no ssa */
+		return 0;
 	} else {
 		ASSERT_MSG("Invalid return value of 'get_sum_entry'");
 	}
-
-	return 1;
+	return -EINVAL;
 }
 
 static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
@@ -177,114 +173,145 @@
 	return 1;
 }
 
-int fsck_chk_node_blk(struct f2fs_sb_info *sbi,
-		struct f2fs_inode *inode,
-		u32 nid,
-		enum FILE_TYPE ftype,
-		enum NODE_TYPE ntype,
-		u32 *blk_cnt)
+static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid,
+			struct f2fs_node *node_blk,
+			enum FILE_TYPE ftype, enum NODE_TYPE ntype,
+			struct node_info *ni)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
-	struct node_info ni;
-	struct f2fs_node *node_blk = NULL;
-	int ret = 0;
+	int ret;
 
 	if (!IS_VALID_NID(sbi, nid)) {
 		ASSERT_MSG("nid is not valid. [0x%x]", nid);
-		return 0;
+		return -EINVAL;
 	}
 
+	get_node_info(sbi, nid, ni);
+	if (ni->blk_addr == NEW_ADDR) {
+		ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid);
+		return -EINVAL;
+	}
+
+	if (!IS_VALID_BLK_ADDR(sbi, ni->blk_addr)) {
+		ASSERT_MSG("blkaddres is not valid. [0x%x]", ni->blk_addr);
+		return -EINVAL;
+	}
+
+	if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) {
+		ASSERT_MSG("summary node block is not valid. [0x%x]", nid);
+		return -EINVAL;
+	}
+
+	ret = dev_read_block(node_blk, ni->blk_addr);
+	ASSERT(ret >= 0);
+
+	if (ntype == TYPE_INODE &&
+			node_blk->footer.nid != node_blk->footer.ino) {
+		ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]",
+				nid, le32_to_cpu(node_blk->footer.nid),
+				le32_to_cpu(node_blk->footer.ino));
+		return -EINVAL;
+	}
+	if (ntype != TYPE_INODE &&
+			node_blk->footer.nid == node_blk->footer.ino) {
+		ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]",
+				nid, le32_to_cpu(node_blk->footer.nid),
+				le32_to_cpu(node_blk->footer.ino));
+		return -EINVAL;
+	}
+
+	if (le32_to_cpu(node_blk->footer.nid) != nid) {
+		ASSERT_MSG("nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]",
+				nid, ni->blk_addr,
+				le32_to_cpu(node_blk->footer.nid));
+		return -EINVAL;
+	}
+
+	if (ntype == TYPE_XATTR) {
+		u32 flag = le32_to_cpu(node_blk->footer.flag);
+
+		if ((flag >> OFFSET_BIT_SHIFT) != XATTR_NODE_OFFSET) {
+			ASSERT_MSG("xnid[0x%x] has wrong ofs:[0x%x]",
+					nid, flag);
+			return -EINVAL;
+		}
+	}
+
+	if ((ntype == TYPE_INODE && ftype == F2FS_FT_DIR) ||
+			(ntype == TYPE_XATTR && ftype == F2FS_FT_XATTR)) {
+		/* not included '.' & '..' */
+		if (f2fs_test_main_bitmap(sbi, ni->blk_addr) != 0) {
+			ASSERT_MSG("Duplicated node blk. nid[0x%x][0x%x]\n",
+					nid, ni->blk_addr);
+			return -EINVAL;
+		}
+	}
+
+	/* workaround to fix later */
 	if (ftype != F2FS_FT_ORPHAN ||
-			f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0x0)
+			f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0)
 		f2fs_clear_bit(nid, fsck->nat_area_bitmap);
 	else
-		ASSERT_MSG("nid duplicated [0x%x]\n", nid);
+		ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n",
+				nid);
 
-	get_node_info(sbi, nid, &ni);
+	if (f2fs_test_sit_bitmap(sbi, ni->blk_addr) == 0)
+		ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]",
+				ni->blk_addr);
 
-	/* Is it reserved block?
-	 * if block addresss was 0xffff,ffff,ffff,ffff
-	 * it means that block was already allocated, but not stored in disk
-	 */
-	if (ni.blk_addr == NEW_ADDR) {
-		fsck->chk.valid_blk_cnt++;
-		fsck->chk.valid_node_cnt++;
-		if (ntype == TYPE_INODE)
-			fsck->chk.valid_inode_cnt++;
-		return 0;
-	}
-
-	if (!IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) {
-		ASSERT_MSG("blkaddres is not valid. [0x%x]", ni.blk_addr);
-		return 0;
-	}
-
-	is_valid_ssa_node_blk(sbi, nid, ni.blk_addr);
-
-	if (f2fs_test_sit_bitmap(sbi, ni.blk_addr) == 0)
-		ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", ni.blk_addr);
-
-	if (f2fs_test_main_bitmap(sbi, ni.blk_addr) == 0) {
+	if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
 		fsck->chk.valid_blk_cnt++;
 		fsck->chk.valid_node_cnt++;
 	}
+	return 0;
+}
+
+int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
+		u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
+		u32 *blk_cnt)
+{
+	struct node_info ni;
+	struct f2fs_node *node_blk = NULL;
 
 	node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
 	ASSERT(node_blk != NULL);
 
-	ret = dev_read_block(node_blk, ni.blk_addr);
-	ASSERT(ret >= 0);
-
-	if (nid != le32_to_cpu(node_blk->footer.nid))
-		ASSERT_MSG("nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]",
-			nid, ni.blk_addr, le32_to_cpu(node_blk->footer.nid));
+	if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni))
+		goto err;
 
 	if (ntype == TYPE_INODE) {
-		ret = fsck_chk_inode_blk(sbi,
-				nid,
-				ftype,
-				node_blk,
-				blk_cnt,
-				&ni);
+		fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, &ni);
 	} else {
-		/* it's not inode */
-		ASSERT(node_blk->footer.nid != node_blk->footer.ino);
-
-		if (f2fs_test_main_bitmap(sbi, ni.blk_addr) != 0)
-			ASSERT_MSG("Duplicated node blk. nid[0x%x][0x%x]\n",
-						nid, ni.blk_addr);
-
 		f2fs_set_main_bitmap(sbi, ni.blk_addr);
 
 		switch (ntype) {
 		case TYPE_DIRECT_NODE:
-			ret = fsck_chk_dnode_blk(sbi, inode, nid, ftype,
-					node_blk, blk_cnt, &ni);
+			fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk,
+					blk_cnt, &ni);
 			break;
 		case TYPE_INDIRECT_NODE:
-			ret = fsck_chk_idnode_blk(sbi, inode, ftype, node_blk,
+			fsck_chk_idnode_blk(sbi, inode, ftype, node_blk,
 					blk_cnt);
 			break;
 		case TYPE_DOUBLE_INDIRECT_NODE:
-			ret = fsck_chk_didnode_blk(sbi, inode, ftype, node_blk,
+			fsck_chk_didnode_blk(sbi, inode, ftype, node_blk,
 					blk_cnt);
 			break;
 		default:
 			ASSERT(0);
 		}
 	}
-	ASSERT(ret >= 0);
-
 	free(node_blk);
 	return 0;
+err:
+	free(node_blk);
+	return -EINVAL;
 }
 
-int fsck_chk_inode_blk(struct f2fs_sb_info *sbi,
-		u32 nid,
-		enum FILE_TYPE ftype,
-		struct f2fs_node *node_blk,
-		u32 *blk_cnt,
-		struct node_info *ni)
+/* start with valid nid and blkaddr */
+void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
+		enum FILE_TYPE ftype, struct f2fs_node *node_blk,
+		u32 *blk_cnt, struct node_info *ni)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	u32 child_cnt = 0, child_files = 0;
@@ -292,31 +319,13 @@
 	u32 i_links = le32_to_cpu(node_blk->i.i_links);
 	u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks);
 	unsigned int idx = 0;
-	int ret = 0;
-
-	ASSERT(node_blk->footer.nid == node_blk->footer.ino);
-	ASSERT(le32_to_cpu(node_blk->footer.nid) == nid);
+	int ret;
 
 	if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0)
 		fsck->chk.valid_inode_cnt++;
 
-	/* Orphan node. i_links should be 0 */
-	if (ftype == F2FS_FT_ORPHAN) {
-		ASSERT(i_links == 0);
-	} else {
-		ASSERT(i_links > 0);
-	}
-
 	if (ftype == F2FS_FT_DIR) {
-
-		/* not included '.' & '..' */
-		if (f2fs_test_main_bitmap(sbi, ni->blk_addr) != 0) {
-			DBG(0, "Duplicated inode blk. ino[0x%x][0x%x]\n",
-							nid, ni->blk_addr);
-			ASSERT(0);
-		}
 		f2fs_set_main_bitmap(sbi, ni->blk_addr);
-
 	} else {
 		if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
 			f2fs_set_main_bitmap(sbi, ni->blk_addr);
@@ -326,21 +335,15 @@
 				fsck->chk.multi_hard_link_files++;
 			}
 		} else {
-			if (i_links <= 1) {
-				DBG(0, "Error. Node ID [0x%x]."
-					" There are one more hard links."
-					" But i_links is [0x%x]\n",
+			DBG(3, "[0x%x] has hard links [0x%x]\n", nid, i_links);
+			if (find_and_dec_hard_link_list(sbi, nid)) {
+				ASSERT_MSG("[0x%x] needs more i_links=0x%x",
 						nid, i_links);
-				ASSERT(0);
+				if (config.fix_cnt)
+					printf("TODO: i_links++\n");
 			}
-
-			DBG(3, "ino[0x%x] has hard links [0x%x]\n",
-						nid, i_links);
-			ret = find_and_dec_hard_link_list(sbi, nid);
-			ASSERT(ret >= 0);
-
 			/* No need to go deep into the node */
-			goto out;
+			return;
 		}
 	}
 
@@ -358,13 +361,13 @@
 	/* check data blocks in inode */
 	for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) {
 		if (le32_to_cpu(node_blk->i.i_addr[idx]) != 0) {
-			*blk_cnt = *blk_cnt + 1;
 			ret = fsck_chk_data_blk(sbi,
 					le32_to_cpu(node_blk->i.i_addr[idx]),
 					&child_cnt, &child_files,
 					(i_blocks == *blk_cnt),
 					ftype, nid, idx, ni->version);
-			ASSERT(ret >= 0);
+			if (!ret)
+				*blk_cnt = *blk_cnt + 1;
 		}
 	}
 
@@ -380,11 +383,13 @@
 			ASSERT(0);
 
 		if (le32_to_cpu(node_blk->i.i_nid[idx]) != 0) {
-			*blk_cnt = *blk_cnt + 1;
 			ret = fsck_chk_node_blk(sbi, &node_blk->i,
 					le32_to_cpu(node_blk->i.i_nid[idx]),
 					ftype, ntype, blk_cnt);
-			ASSERT(ret >= 0);
+			if (!ret)
+				*blk_cnt = *blk_cnt + 1;
+			else if (config.fix_cnt)
+				printf("TODO delete i_nid[idx] = 0;\n");
 		}
 	}
 check:
@@ -400,35 +405,46 @@
 				node_blk->i.i_name,
 				(u32)i_blocks);
 	if ((ftype == F2FS_FT_DIR && i_links != child_cnt) ||
-			(i_blocks != *blk_cnt)) {
-		print_node_info(node_blk);
+						(i_blocks != *blk_cnt)) {
+		if (!config.fix_cnt)
+			print_node_info(node_blk);
+
+		/* node_blk, ni.blkaddr, child_cnt, *blk_cnt */
+		if (config.fix_cnt)
+			printf("TODO fix_inode_block\n");
+		else
+			print_node_info(node_blk);
 		DBG(1, "blk   cnt [0x%x]\n", *blk_cnt);
 		DBG(1, "child cnt [0x%x]\n", child_cnt);
 	}
-
-	ASSERT(i_blocks == *blk_cnt);
-	if (ftype == F2FS_FT_DIR)
-		ASSERT(i_links == child_cnt);
-out:
-	return 0;
+	if (i_blocks != *blk_cnt)
+		ASSERT_MSG("ino: 0x%x has i_blocks: %lu, but has %u blocks",
+						nid, i_blocks, *blk_cnt);
+	if (ftype == F2FS_FT_DIR && i_links != child_cnt)
+		ASSERT_MSG("ino: 0x%x has i_links: %u but real links: %u",
+						nid, i_links, child_cnt);
+	if (ftype == F2FS_FT_ORPHAN && i_links)
+		ASSERT_MSG("ino: 0x%x is orphan inode, but has i_links: %u",
+						nid, i_links);
 }
 
 int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 		u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk,
 		u32 *blk_cnt, struct node_info *ni)
 {
-	int idx;
+	int idx, ret;
 	u32 child_cnt = 0, child_files = 0;
 
 	for (idx = 0; idx < ADDRS_PER_BLOCK; idx++) {
 		if (le32_to_cpu(node_blk->dn.addr[idx]) == 0x0)
 			continue;
-		*blk_cnt = *blk_cnt + 1;
-		fsck_chk_data_blk(sbi,
+		ret = fsck_chk_data_blk(sbi,
 			le32_to_cpu(node_blk->dn.addr[idx]),
 			&child_cnt, &child_files,
 			le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
 			nid, idx, ni->version);
+		if (!ret)
+			*blk_cnt = *blk_cnt + 1;
 	}
 	return 0;
 }
@@ -436,14 +452,19 @@
 int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt)
 {
+	int ret;
 	int i = 0;
 
 	for (i = 0 ; i < NIDS_PER_BLOCK; i++) {
 		if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
 			continue;
-		*blk_cnt = *blk_cnt + 1;
-		fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]),
+		ret = fsck_chk_node_blk(sbi, inode,
+				le32_to_cpu(node_blk->in.nid[i]),
 				ftype, TYPE_DIRECT_NODE, blk_cnt);
+		if (!ret)
+			*blk_cnt = *blk_cnt + 1;
+		else if (ret == -EINVAL)
+			printf("delete in.nid[i] = 0;\n");
 	}
 	return 0;
 }
@@ -452,14 +473,18 @@
 		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt)
 {
 	int i = 0;
+	int ret = 0;
 
 	for (i = 0; i < NIDS_PER_BLOCK; i++) {
 		if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
 			continue;
-		*blk_cnt = *blk_cnt + 1;
-		fsck_chk_node_blk(sbi, inode,
+		ret = fsck_chk_node_blk(sbi, inode,
 				le32_to_cpu(node_blk->in.nid[i]),
 				ftype, TYPE_INDIRECT_NODE, blk_cnt);
+		if (!ret)
+			*blk_cnt = *blk_cnt + 1;
+		else if (ret == -EINVAL)
+			printf("delete in.nid[i] = 0;\n");
 	}
 	return 0;
 }
@@ -570,7 +595,8 @@
 				TYPE_INODE,
 				&blk_cnt);
 
-		ASSERT(ret >= 0);
+		if (ret)
+			printf("TODO: delete dentry\n");
 
 		i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
 		dentries++;
@@ -618,35 +644,34 @@
 
 	fsck->chk.valid_blk_cnt++;
 
-	if (ftype == F2FS_FT_DIR) {
-		fsck_chk_dentry_blk(sbi, blk_addr, child_cnt,
+	if (ftype == F2FS_FT_DIR)
+		return fsck_chk_dentry_blk(sbi, blk_addr, child_cnt,
 				child_files, last_blk);
-	}
-
 	return 0;
 }
 
-int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
+void fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
 {
-	int ret = 0;
 	u32 blk_cnt = 0;
-
 	block_t start_blk, orphan_blkaddr, i, j;
 	struct f2fs_orphan_block *orphan_blk;
 	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
 
 	if (!is_set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG))
-		return 0;
+		return;
+
+	if (config.fix_cnt)
+		return;
 
 	start_blk = __start_cp_addr(sbi) + 1 +
 		le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
-
 	orphan_blkaddr = __start_sum_addr(sbi) - 1;
-
 	orphan_blk = calloc(BLOCK_SZ, 1);
 
 	for (i = 0; i < orphan_blkaddr; i++) {
-		dev_read_block(orphan_blk, start_blk + i);
+		int ret = dev_read_block(orphan_blk, start_blk + i);
+
+		ASSERT(ret >= 0);
 
 		for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
 			nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
@@ -654,47 +679,42 @@
 			blk_cnt = 1;
 			ret = fsck_chk_node_blk(sbi, NULL, ino,
 					F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt);
-			ASSERT(ret >= 0);
+			if (ret == -EINVAL)
+				printf("TODO: nothing?\n");
 		}
 		memset(orphan_blk, 0, BLOCK_SZ);
 	}
 	free(orphan_blk);
-	return 0;
 }
 
-int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino,
-					u32 x_nid, u32 *blk_cnt)
+void fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino,
+		u32 x_nid, u32 *blk_cnt)
 {
-	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct f2fs_node *node_blk = NULL;
 	struct node_info ni;
 
 	if (x_nid == 0x0)
-		return 0;
+		return;
 
-	if (f2fs_test_bit(x_nid, fsck->nat_area_bitmap) != 0x0) {
-		f2fs_clear_bit(x_nid, fsck->nat_area_bitmap);
-	} else {
-		ASSERT_MSG("xattr_nid duplicated [0x%x]\n", x_nid);
+	node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+	ASSERT(node_blk != NULL);
+
+	/* Sanity check */
+	if (sanity_check_nid(sbi, x_nid, node_blk,
+				F2FS_FT_XATTR, TYPE_XATTR, &ni)) {
+		/* TODO: drop xattr node */
+		printf("drop xattr node\n");
+		goto out;
 	}
 
 	*blk_cnt = *blk_cnt + 1;
-	fsck->chk.valid_blk_cnt++;
-	fsck->chk.valid_node_cnt++;
-
-	get_node_info(sbi, x_nid, &ni);
-
-	if (f2fs_test_main_bitmap(sbi, ni.blk_addr) != 0) {
-		ASSERT_MSG("Duplicated node block for x_attr. "
-				"x_nid[0x%x] block addr[0x%x]\n",
-				x_nid, ni.blk_addr);
-	}
 	f2fs_set_main_bitmap(sbi, ni.blk_addr);
-
 	DBG(2, "ino[0x%x] x_nid[0x%x]\n", ino, x_nid);
-	return 0;
+out:
+	free(node_blk);
 }
 
-int fsck_init(struct f2fs_sb_info *sbi)
+void fsck_init(struct f2fs_sb_info *sbi)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
 	struct f2fs_sm_info *sm_i = SM_I(sbi);
@@ -718,7 +738,7 @@
 	build_sit_area_bitmap(sbi);
 
 	tree_mark = calloc(tree_mark_size, 1);
-	return 0;
+	ASSERT(tree_mark != NULL);
 }
 
 int fsck_verify(struct f2fs_sb_info *sbi)
diff --git a/fsck/fsck.h b/fsck/fsck.h
index b673646..aecfa9a 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -59,7 +59,8 @@
 	TYPE_INODE = 37,
 	TYPE_DIRECT_NODE = 43,
 	TYPE_INDIRECT_NODE = 53,
-	TYPE_DOUBLE_INDIRECT_NODE = 67
+	TYPE_DOUBLE_INDIRECT_NODE = 67,
+	TYPE_XATTR = 77
 };
 
 struct hard_link_node {
@@ -76,11 +77,11 @@
 	SEG_TYPE_MAX,
 };
 
-extern int fsck_chk_xattr_blk(struct f2fs_sb_info *, u32, u32, u32 *);
-extern int fsck_chk_orphan_node(struct f2fs_sb_info *);
+extern void fsck_chk_xattr_blk(struct f2fs_sb_info *, u32, u32, u32 *);
+extern void fsck_chk_orphan_node(struct f2fs_sb_info *);
 extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
 		enum FILE_TYPE, enum NODE_TYPE, u32 *);
-extern int fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE,
+extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE,
 		struct f2fs_node *, u32 *, struct node_info *);
 extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
 		u32, enum FILE_TYPE, struct f2fs_node *, u32 *,
@@ -102,7 +103,7 @@
 extern void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
 extern void build_nat_area_bitmap(struct f2fs_sb_info *);
 extern int build_sit_area_bitmap(struct f2fs_sb_info *);
-extern int fsck_init(struct f2fs_sb_info *);
+extern void fsck_init(struct f2fs_sb_info *);
 extern int fsck_verify(struct f2fs_sb_info *);
 extern void fsck_free(struct f2fs_sb_info *);
 extern int f2fs_do_mount(struct f2fs_sb_info *);
diff --git a/fsck/main.c b/fsck/main.c
index 266e9b5..7e3bb49 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -131,44 +131,29 @@
 	config.device_name = argv[optind];
 }
 
-int do_fsck(struct f2fs_sb_info *sbi)
+static void do_fsck(struct f2fs_sb_info *sbi)
 {
 	u32 blk_cnt;
-	int ret;
 
 	config.bug_on = 0;
 
-	ret = fsck_init(sbi);
-	if (ret < 0)
-		return ret;
+	fsck_init(sbi);
 
 	fsck_chk_orphan_node(sbi);
 
 	/* Traverse all block recursively from root inode */
 	blk_cnt = 1;
-	ret = fsck_chk_node_blk(sbi,
-			NULL,
-			sbi->root_ino_num,
-			F2FS_FT_DIR,
-			TYPE_INODE,
-			&blk_cnt);
-	if (ret < 0)
-		goto out1;
-
-	ret = fsck_verify(sbi);
-out1:
+	fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
+			F2FS_FT_DIR, TYPE_INODE, &blk_cnt);
+	fsck_verify(sbi);
 	fsck_free(sbi);
-	return ret;
 }
 
-int do_dump(struct f2fs_sb_info *sbi)
+static void do_dump(struct f2fs_sb_info *sbi)
 {
 	struct dump_option *opt = (struct dump_option *)config.private;
-	int ret;
 
-	ret = fsck_init(sbi);
-	if (ret < 0)
-		return ret;
+	fsck_init(sbi);
 
 	if (opt->end_sit == -1)
 		opt->end_sit = SM_I(sbi)->main_segments;
@@ -182,15 +167,12 @@
 		dump_inode_from_blkaddr(sbi, opt->blk_addr);
 		goto cleanup;
 	}
-
 	dump_node(sbi, opt->nid);
-
 cleanup:
 	fsck_free(sbi);
-	return 0;
 }
 
-int main (int argc, char **argv)
+int main(int argc, char **argv)
 {
 	struct f2fs_sb_info *sbi;
 	int ret = 0;
@@ -215,10 +197,10 @@
 
 	switch (config.func) {
 	case FSCK:
-		ret = do_fsck(sbi);
+		do_fsck(sbi);
 		break;
 	case DUMP:
-		ret = do_dump(sbi);
+		do_dump(sbi);
 		break;
 	}
 
@@ -248,5 +230,5 @@
 	f2fs_finalize_device(&config);
 
 	printf("\nDone.\n");
-	return ret;
+	return 0;
 }
diff --git a/fsck/mount.c b/fsck/mount.c
index ab5f7f3..a766157 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -1020,7 +1020,8 @@
 			struct node_info ni;
 			ni.nid = nid + i;
 
-			if ((nid + i) == F2FS_NODE_INO(sbi) || (nid + i) == F2FS_META_INO(sbi)) {
+			if ((nid + i) == F2FS_NODE_INO(sbi) ||
+					(nid + i) == F2FS_META_INO(sbi)) {
 				ASSERT(nat_block->entries[i].block_addr != 0x0);
 				continue;
 			}
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index dace9aa..c3cc2aa 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -413,6 +413,9 @@
 	OFFSET_BIT_SHIFT
 };
 
+#define XATTR_NODE_OFFSET	((((unsigned int)-1) << OFFSET_BIT_SHIFT) \
+				>> OFFSET_BIT_SHIFT)
+
 struct node_footer {
 	__le32 nid;		/* node id */
 	__le32 ino;		/* inode nunmber */
@@ -640,6 +643,7 @@
 	F2FS_FT_MAX,
 	/* added for fsck */
 	F2FS_FT_ORPHAN,
+	F2FS_FT_XATTR,
 };
 
 /* from f2fs/segment.h */