diff options
Diffstat (limited to 'fsck/fsck.c')
-rw-r--r-- | fsck/fsck.c | 833 |
1 files changed, 499 insertions, 334 deletions
diff --git a/fsck/fsck.c b/fsck/fsck.c index ba29ab7..d0819c4 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -11,9 +11,32 @@ #include "fsck.h" char *tree_mark; -int tree_mark_size = 256; +uint32_t tree_mark_size = 256; -static int add_into_hard_link_list(struct f2fs_sb_info *sbi, u32 nid, u32 link_cnt) +static inline int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->main_area_bitmap); +} + +static inline int f2fs_test_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + return f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk), + fsck->main_area_bitmap); +} + +static inline int f2fs_test_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + return f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap); +} + +static int add_into_hard_link_list(struct f2fs_sb_info *sbi, + u32 nid, u32 link_cnt) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *node = NULL, *tmp = NULL, *prev = NULL; @@ -57,10 +80,8 @@ static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid) 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; @@ -69,10 +90,8 @@ static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid) 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; @@ -85,40 +104,38 @@ static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid) prev->next = node->next; free(node); } - return 0; - } -static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, u32 blk_addr) +static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, + u32 blk_addr) { int ret = 0; struct f2fs_summary sum_entry; ret = get_sum_entry(sbi, blk_addr, &sum_entry); - ASSERT(ret >= 0); - if (ret == SEG_TYPE_DATA || ret == SEG_TYPE_CUR_DATA) { - ASSERT_MSG(0, "Summary footer is not a node segment summary\n");; - } else if (ret == SEG_TYPE_NODE) { - if (le32_to_cpu(sum_entry.nid) != nid) { - DBG(0, "nid [0x%x]\n", nid); - DBG(0, "target blk_addr [0x%x]\n", blk_addr); - DBG(0, "summary blk_addr [0x%x]\n", - GET_SUM_BLKADDR(sbi, GET_SEGNO(sbi, blk_addr))); - DBG(0, "seg no / offset [0x%x / 0x%x]\n", - GET_SEGNO(sbi, blk_addr), OFFSET_IN_SEG(sbi, blk_addr)); - DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry.nid)); - DBG(0, "--> node block's nid [0x%x]\n", nid); - ASSERT_MSG(0, "Invalid node seg summary\n"); - } - } else if (ret == SEG_TYPE_CUR_NODE) { - /* current node segment has no ssa */ - } else { - ASSERT_MSG(0, "Invalid return value of 'get_sum_entry'"); + if (ret != SEG_TYPE_NODE && ret != SEG_TYPE_CUR_NODE) { + ASSERT_MSG("Summary footer is not for node segment"); + return -EINVAL; + } + + if (le32_to_cpu(sum_entry.nid) != nid) { + DBG(0, "nid [0x%x]\n", nid); + DBG(0, "target blk_addr [0x%x]\n", blk_addr); + DBG(0, "summary blk_addr [0x%x]\n", + GET_SUM_BLKADDR(sbi, + GET_SEGNO(sbi, blk_addr))); + DBG(0, "seg no / offset [0x%x / 0x%x]\n", + GET_SEGNO(sbi, blk_addr), + OFFSET_IN_SEG(sbi, blk_addr)); + DBG(0, "summary_entry.nid [0x%x]\n", + le32_to_cpu(sum_entry.nid)); + DBG(0, "--> node block's nid [0x%x]\n", nid); + ASSERT_MSG("Invalid node seg summary\n"); + return -EINVAL; } - - return 1; + return 0; } static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, @@ -128,204 +145,250 @@ static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, struct f2fs_summary sum_entry; ret = get_sum_entry(sbi, blk_addr, &sum_entry); - ASSERT(ret == SEG_TYPE_DATA || ret == SEG_TYPE_CUR_DATA); + + if (ret != SEG_TYPE_DATA && ret != SEG_TYPE_CUR_DATA) { + ASSERT_MSG("Summary footer is not for data segment"); + return -EINVAL; + } if (le32_to_cpu(sum_entry.nid) != parent_nid || sum_entry.version != version || le16_to_cpu(sum_entry.ofs_in_node) != idx_in_node) { - DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry.nid)); - DBG(0, "summary_entry.version [0x%x]\n", sum_entry.version); - DBG(0, "summary_entry.ofs_in_node [0x%x]\n", le16_to_cpu(sum_entry.ofs_in_node)); - + DBG(0, "summary_entry.nid [0x%x]\n", + le32_to_cpu(sum_entry.nid)); + DBG(0, "summary_entry.version [0x%x]\n", + sum_entry.version); + DBG(0, "summary_entry.ofs_in_node [0x%x]\n", + le16_to_cpu(sum_entry.ofs_in_node)); DBG(0, "parent nid [0x%x]\n", parent_nid); DBG(0, "version from nat [0x%x]\n", version); DBG(0, "idx in parent node [0x%x]\n", idx_in_node); DBG(0, "Target data block addr [0x%x]\n", blk_addr); - ASSERT_MSG(0, "Invalid data seg summary\n"); + ASSERT_MSG("Invalid data seg summary\n"); + return -EINVAL; } - - return 1; + 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) +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 -EINVAL; + } - IS_VALID_NID(sbi, nid); + get_node_info(sbi, nid, ni); + if (ni->blk_addr == NEW_ADDR) { + ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid); + return -EINVAL; + } - if (ftype != F2FS_FT_ORPHAN || - f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0x0) - f2fs_clear_bit(nid, fsck->nat_area_bitmap); - else - ASSERT_MSG(0, "nid duplicated [0x%x]\n", nid); + if (!IS_VALID_BLK_ADDR(sbi, ni->blk_addr)) { + ASSERT_MSG("blkaddres is not valid. [0x%x]", ni->blk_addr); + return -EINVAL; + } - ret = get_node_info(sbi, nid, &ni); + 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); - /* 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 (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; } - IS_VALID_BLK_ADDR(sbi, ni.blk_addr); + 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; + } - is_valid_ssa_node_blk(sbi, nid, ni.blk_addr); + if (ntype == TYPE_XATTR) { + u32 flag = le32_to_cpu(node_blk->footer.flag); - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->sit_area_bitmap) == 0x0) { - DBG(0, "SIT bitmap is 0x0. blk_addr[0x%x]\n", ni.blk_addr); - ASSERT(0); + 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; + } } - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) == 0x0) { + /* workaround to fix later */ + if (ftype != F2FS_FT_ORPHAN || + f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0) + f2fs_clear_bit(nid, fsck->nat_area_bitmap); + else + ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n", + nid); + + 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) { fsck->chk.valid_blk_cnt++; fsck->chk.valid_node_cnt++; } + return 0; +} + +static int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino, + u32 x_nid, u32 *blk_cnt) +{ + struct f2fs_node *node_blk = NULL; + struct node_info ni; + int ret = 0; + + if (x_nid == 0x0) + return 0; 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); + /* Sanity check */ + if (sanity_check_nid(sbi, x_nid, node_blk, + F2FS_FT_XATTR, TYPE_XATTR, &ni)) { + ret = -EINVAL; + goto out; + } + + *blk_cnt = *blk_cnt + 1; + f2fs_set_main_bitmap(sbi, ni.blk_addr); + DBG(2, "ino[0x%x] x_nid[0x%x]\n", ino, x_nid); +out: + free(node_blk); + return ret; +} - ASSERT_MSG(nid == le32_to_cpu(node_blk->footer.nid), - "nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]\n", - nid, ni.blk_addr, le32_to_cpu(node_blk->footer.nid)); +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); + + 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_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) != 0) { - DBG(0, "Duplicated node block. ino[0x%x][0x%x]\n", nid, ni.blk_addr); - ASSERT(0); - } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap); + 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, - nid, - 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, - nid, - 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; enum NODE_TYPE ntype; u32 i_links = le32_to_cpu(node_blk->i.i_links); u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks); - int idx = 0; - int ret = 0; - - ASSERT(node_blk->footer.nid == node_blk->footer.ino); - ASSERT(le32_to_cpu(node_blk->footer.nid) == nid); + unsigned int idx = 0; + int need_fix = 0; + int ret; - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) == 0x0) + 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_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) != 0) { - DBG(0, "Duplicated inode blk. ino[0x%x][0x%x]\n", nid, ni->blk_addr); - ASSERT(0); - } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap); - + f2fs_set_main_bitmap(sbi, ni->blk_addr); } else { - - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) == 0x0) { - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap); + if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { + f2fs_set_main_bitmap(sbi, ni->blk_addr); if (i_links > 1) { /* First time. Create new hard link node */ add_into_hard_link_list(sbi, nid, i_links); 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_on) { + node_blk->i.i_links = + cpu_to_le32(i_links + 1); + need_fix = 1; + FIX_MSG("File: 0x%x " + "i_links= 0x%x -> 0x%x", + nid, i_links, i_links + 1); + } } - - 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; } } - fsck_chk_xattr_blk(sbi, nid, le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt); + if (fsck_chk_xattr_blk(sbi, nid, + le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt) && + config.fix_on) { + node_blk->i.i_xattr_nid = 0; + need_fix = 1; + FIX_MSG("Remove xattr block: 0x%x, x_nid = 0x%x", + nid, le32_to_cpu(node_blk->i.i_xattr_nid)); + } if (ftype == F2FS_FT_CHRDEV || ftype == F2FS_FT_BLKDEV || ftype == F2FS_FT_FIFO || ftype == F2FS_FT_SOCK) @@ -338,18 +401,18 @@ int fsck_chk_inode_blk(struct f2fs_sb_info *sbi, /* 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, - &node_blk->i, le32_to_cpu(node_blk->i.i_addr[idx]), - &child_cnt, - &child_files, + &child_cnt, &child_files, (i_blocks == *blk_cnt), - ftype, - nid, - idx, - ni->version); - ASSERT(ret >= 0); + ftype, nid, idx, ni->version); + if (!ret) { + *blk_cnt = *blk_cnt + 1; + } else if (config.fix_on) { + node_blk->i.i_addr[idx] = 0; + need_fix = 1; + FIX_MSG("[0x%x] i_addr[%d] = 0", nid, idx); + } } } @@ -365,114 +428,120 @@ int fsck_chk_inode_blk(struct f2fs_sb_info *sbi, 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, + 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); + ftype, ntype, blk_cnt); + if (!ret) { + *blk_cnt = *blk_cnt + 1; + } else if (config.fix_on) { + node_blk->i.i_nid[idx] = 0; + need_fix = 1; + FIX_MSG("[0x%x] i_nid[%d] = 0", nid, idx); + } } } check: if (ftype == F2FS_FT_DIR) - DBG(1, "Directory Inode: ino: %x name: %s depth: %d child files: %d\n\n", - le32_to_cpu(node_blk->footer.ino), node_blk->i.i_name, - le32_to_cpu(node_blk->i.i_current_depth), child_files); + DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n", + le32_to_cpu(node_blk->footer.ino), + node_blk->i.i_name, + le32_to_cpu(node_blk->i.i_current_depth), + child_files); if (ftype == F2FS_FT_ORPHAN) - DBG(1, "Orphan Inode: ino: %x name: %s i_blocks: %lu\n\n", - le32_to_cpu(node_blk->footer.ino), node_blk->i.i_name, - i_blocks); - if ((ftype == F2FS_FT_DIR && i_links != child_cnt) || - (i_blocks != *blk_cnt)) { - print_node_info(node_blk); - DBG(1, "blk cnt [0x%x]\n", *blk_cnt); - DBG(1, "child cnt [0x%x]\n", child_cnt); + DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n", + le32_to_cpu(node_blk->footer.ino), + node_blk->i.i_name, + (u32)i_blocks); + + if (i_blocks != *blk_cnt) { + ASSERT_MSG("ino: 0x%x has i_blocks: %08"PRIx64", " + "but has %u blocks", + nid, i_blocks, *blk_cnt); + if (config.fix_on) { + node_blk->i.i_blocks = cpu_to_le64(*blk_cnt); + need_fix = 1; + FIX_MSG("[0x%x] i_blocks=0x%08"PRIx64" -> 0x%x", + nid, i_blocks, *blk_cnt); + } + } + if (ftype == F2FS_FT_DIR && i_links != child_cnt) { + ASSERT_MSG("ino: 0x%x has i_links: %u but real links: %u", + nid, i_links, child_cnt); + if (config.fix_on) { + node_blk->i.i_links = cpu_to_le32(child_cnt); + need_fix = 1; + FIX_MSG("Dir: 0x%x i_links= 0x%x -> 0x%x", + nid, i_links, child_cnt); + } } - ASSERT(i_blocks == *blk_cnt); - if (ftype == F2FS_FT_DIR) - ASSERT(i_links == child_cnt); -out: - return 0; + if (ftype == F2FS_FT_ORPHAN && i_links) + ASSERT_MSG("ino: 0x%x is orphan inode, but has i_links: %u", + nid, i_links); + if (need_fix) { + ret = dev_write_block(node_blk, ni->blk_addr); + ASSERT(ret >= 0); + } } -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 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, - inode, - 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); + 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; } -int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - u32 nid, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt) +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, + ret = fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]), - ftype, - TYPE_DIRECT_NODE, - blk_cnt); + 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; } -int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - u32 nid, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt) +int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, + enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt) { 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); + 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; } @@ -482,7 +551,7 @@ static void print_dentry(__u32 depth, __u8 *name, int last_de = 0; int next_idx = 0; int name_len; - int i; + unsigned int i; int bit_offset; if (config.dbg_lv != -1) @@ -512,15 +581,12 @@ static void print_dentry(__u32 depth, __u8 *name, for (i = 1; i < depth; i++) printf("%c ", tree_mark[i]); - printf("%c-- %s\n", last_de ? '`' : '|', name); + printf("%c-- %s 0x%x\n", last_de ? '`' : '|', + name, le32_to_cpu(de_blk->dentry[idx].ino)); } -int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - u32 blk_addr, - u32 *child_cnt, - u32 *child_files, - int last_blk) +int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr, + u32 *child_cnt, u32 *child_files, int last_blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); int i; @@ -543,16 +609,17 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, fsck->dentry_depth++; for (i = 0; i < NR_DENTRY_IN_BLOCK;) { - if (test_bit(i, (unsigned long *)de_blk->dentry_bitmap) == 0x0) { + if (test_bit(i, (unsigned long *)de_blk->dentry_bitmap) == 0) { i++; continue; } - name_len = le32_to_cpu(de_blk->dentry[i].name_len); + name_len = le16_to_cpu(de_blk->dentry[i].name_len); name = calloc(name_len + 1, 1); memcpy(name, de_blk->filename[i], name_len); + hash_code = f2fs_dentry_hash((const unsigned char *)name, + name_len); - hash_code = f2fs_dentry_hash((const char *)name, name_len); ASSERT(le32_to_cpu(de_blk->dentry[i].hash_code) == hash_code); ftype = de_blk->dentry[i].file_type; @@ -560,15 +627,16 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, /* Becareful. 'dentry.file_type' is not imode. */ if (ftype == F2FS_FT_DIR) { *child_cnt = *child_cnt + 1; - if ((name[0] == '.' && name[1] == '.' && name_len == 2) || - (name[0] == '.' && name_len == 1)) { + if ((name[0] == '.' && name_len == 1) || + (name[0] == '.' && name[1] == '.' && + name_len == 2)) { i++; free(name); continue; } } - DBG(2, "[%3u] - no[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n", + DBG(1, "[%3u]-[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n", fsck->dentry_depth, i, name, name_len, le32_to_cpu(de_blk->dentry[i].ino), de_blk->dentry[i].file_type); @@ -583,7 +651,21 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, TYPE_INODE, &blk_cnt); - ASSERT(ret >= 0); + if (ret && config.fix_on) { + int j; + int slots = (name_len + F2FS_SLOT_LEN - 1) / + F2FS_SLOT_LEN; + for (j = 0; j < slots; j++) + clear_bit(i + j, + (unsigned long *)de_blk->dentry_bitmap); + FIX_MSG("Unlink [0x%x] - %s len[0x%x], type[0x%x]", + le32_to_cpu(de_blk->dentry[i].ino), + name, name_len, + de_blk->dentry[i].file_type); + i += slots; + free(name); + continue; + } i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN; dentries++; @@ -591,24 +673,19 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, free(name); } - DBG(1, "[%3d] Dentry Block [0x%x] Done : dentries:%d in %d slots (len:%d)\n\n", - fsck->dentry_depth, blk_addr, dentries, NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN); + DBG(1, "[%3d] Dentry Block [0x%x] Done : " + "dentries:%d in %d slots (len:%d)\n\n", + fsck->dentry_depth, blk_addr, dentries, + NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN); fsck->dentry_depth--; free(de_blk); return 0; } -int fsck_chk_data_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - u32 blk_addr, - u32 *child_cnt, - u32 *child_files, - int last_blk, - enum FILE_TYPE ftype, - u32 parent_nid, - u16 idx_in_node, - u8 ver) +int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, + u32 *child_cnt, u32 *child_files, int last_blk, + enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); @@ -618,112 +695,80 @@ int fsck_chk_data_blk(struct f2fs_sb_info *sbi, return 0; } - IS_VALID_BLK_ADDR(sbi, blk_addr); - - is_valid_ssa_data_blk(sbi, blk_addr, parent_nid, idx_in_node, ver); - - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->sit_area_bitmap) == 0x0) { - ASSERT_MSG(0, "SIT bitmap is 0x0. blk_addr[0x%x]\n", blk_addr); + if (!IS_VALID_BLK_ADDR(sbi, blk_addr)) { + ASSERT_MSG("blkaddres is not valid. [0x%x]", blk_addr); + return -EINVAL; } - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->main_area_bitmap) != 0) { - ASSERT_MSG(0, "Duplicated data block. pnid[0x%x] idx[0x%x] blk_addr[0x%x]\n", - parent_nid, idx_in_node, blk_addr); + if (is_valid_ssa_data_blk(sbi, blk_addr, parent_nid, + idx_in_node, ver)) { + ASSERT_MSG("summary data block is not valid. [0x%x]", + parent_nid); + return -EINVAL; } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->main_area_bitmap); - fsck->chk.valid_blk_cnt++; + if (f2fs_test_sit_bitmap(sbi, blk_addr) == 0) + ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", blk_addr); - if (ftype == F2FS_FT_DIR) { - fsck_chk_dentry_blk(sbi, - inode, - blk_addr, - child_cnt, - child_files, - last_blk); - } + if (f2fs_test_main_bitmap(sbi, blk_addr) != 0) + ASSERT_MSG("Duplicated data [0x%x]. pnid[0x%x] idx[0x%x]", + blk_addr, parent_nid, idx_in_node); + + f2fs_set_main_bitmap(sbi, blk_addr); + + fsck->chk.valid_blk_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(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG)) - return 0; + if (!is_set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG)) + return; - start_blk = __start_cp_addr(sbi) + 1; - orphan_blkaddr = __start_sum_addr(sbi) - 1; + if (config.fix_on) + 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]); DBG(1, "[%3d] ino [0x%x]\n", i, ino); blk_cnt = 1; - ret = fsck_chk_node_blk(sbi, - NULL, - ino, - F2FS_FT_ORPHAN, - TYPE_INODE, - &blk_cnt); - ASSERT(ret >= 0); + fsck_chk_node_blk(sbi, NULL, ino, + F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt); } 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) -{ - struct f2fs_fsck *fsck = F2FS_FSCK(sbi); - struct node_info ni; - - if (x_nid == 0x0) - return 0; - - if (f2fs_test_bit(x_nid, fsck->nat_area_bitmap) != 0x0) { - f2fs_clear_bit(x_nid, fsck->nat_area_bitmap); - } else { - ASSERT_MSG(0, "xattr_nid duplicated [0x%x]\n", x_nid); - } - - *blk_cnt = *blk_cnt + 1; - fsck->chk.valid_blk_cnt++; - fsck->chk.valid_node_cnt++; - - ASSERT(get_node_info(sbi, x_nid, &ni) >= 0); - - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) != 0) { - ASSERT_MSG(0, "Duplicated node block for x_attr. " - "x_nid[0x%x] block addr[0x%x]\n", - x_nid, ni.blk_addr); - } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap); - - DBG(2, "ino[0x%x] x_nid[0x%x]\n", ino, x_nid); - return 0; -} - -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); /* - * We build three bitmap for main/sit/nat so that may check consistency of filesystem. - * 1. main_area_bitmap will be used to check whether all blocks of main area is used or not. + * We build three bitmap for main/sit/nat so that may check consistency + * of filesystem. + * 1. main_area_bitmap will be used to check whether all blocks of main + * area is used or not. * 2. nat_area_bitmap has bitmap information of used nid in NAT. * 3. sit_area_bitmap has bitmap information of used main block. * At Last sequence, we compare main_area_bitmap with sit_area_bitmap. @@ -738,12 +783,89 @@ int fsck_init(struct f2fs_sb_info *sbi) build_sit_area_bitmap(sbi); tree_mark = calloc(tree_mark_size, 1); + ASSERT(tree_mark != NULL); +} + +static void fix_nat_entries(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + u32 i; + + for (i = 0; i < fsck->nr_nat_entries; i++) + if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) + nullify_nat_entry(sbi, i); +} + +static void fix_checkpoint(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct f2fs_super_block *raw_sb = sbi->raw_super; + struct f2fs_checkpoint *ckp = F2FS_CKPT(sbi); + unsigned long long cp_blk_no; + u32 i; + int ret; + u_int32_t crc = 0; + + ckp->ckpt_flags = cpu_to_le32(CP_UMOUNT_FLAG); + ckp->cp_pack_total_block_count = + cpu_to_le32(8 + le32_to_cpu(raw_sb->cp_payload)); + ckp->cp_pack_start_sum = cpu_to_le32(1 + + le32_to_cpu(raw_sb->cp_payload)); + + ckp->free_segment_count = cpu_to_le32(fsck->chk.free_segs); + ckp->valid_block_count = cpu_to_le32(fsck->chk.valid_blk_cnt); + ckp->valid_node_count = cpu_to_le32(fsck->chk.valid_node_cnt); + ckp->valid_inode_count = cpu_to_le32(fsck->chk.valid_inode_cnt); + + crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, ckp, CHECKSUM_OFFSET); + *((__le32 *)((unsigned char *)ckp + CHECKSUM_OFFSET)) = + cpu_to_le32(crc); + + cp_blk_no = le32_to_cpu(raw_sb->cp_blkaddr); + if (sbi->cur_cp == 2) + cp_blk_no += 1 << le32_to_cpu(raw_sb->log_blocks_per_seg); + + ret = dev_write_block(ckp, cp_blk_no++); + ASSERT(ret >= 0); + + for (i = 0; i < le32_to_cpu(raw_sb->cp_payload); i++) { + ret = dev_write_block(((unsigned char *)ckp) + i * F2FS_BLKSIZE, + cp_blk_no++); + ASSERT(ret >= 0); + } + + for (i = 0; i < NO_CHECK_TYPE; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + + ret = dev_write_block(curseg->sum_blk, cp_blk_no++); + ASSERT(ret >= 0); + } + + ret = dev_write_block(ckp, cp_blk_no++); + ASSERT(ret >= 0); +} + +int check_curseg_offset(struct f2fs_sb_info *sbi) +{ + int i; + + for (i = 0; i < NO_CHECK_TYPE; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + struct seg_entry *se; + + se = get_seg_entry(sbi, curseg->segno); + if (f2fs_test_bit(curseg->next_blkoff, + (const char *)se->cur_valid_map) == 1) { + ASSERT_MSG("Next block offset is not free, type:%d", i); + return -EINVAL; + } + } return 0; } int fsck_verify(struct f2fs_sb_info *sbi) { - int i = 0; + unsigned int i = 0; int ret = 0; u32 nr_unref_nid = 0; struct f2fs_fsck *fsck = F2FS_FSCK(sbi); @@ -765,6 +887,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) node->nid, node->links); node = node->next; } + config.bug_on = 1; } printf("[FSCK] Unreachable nat entries "); @@ -773,14 +896,17 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", nr_unref_nid); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] SIT valid block bitmap checking "); - if (memcmp(fsck->sit_area_bitmap, fsck->main_area_bitmap, fsck->sit_area_bitmap_sz) == 0x0) { + if (memcmp(fsck->sit_area_bitmap, fsck->main_area_bitmap, + fsck->sit_area_bitmap_sz) == 0x0) { printf("[Ok..]\n"); } else { printf("[Fail]\n"); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] Hard link checking for regular file "); @@ -789,14 +915,16 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_block_count matching with CP "); if (sbi->total_valid_block_count == fsck->chk.valid_blk_cnt) { - printf(" [Ok..] [0x%lx]\n", fsck->chk.valid_blk_cnt); + printf(" [Ok..] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); } else { - printf(" [Fail] [0x%lx]\n", fsck->chk.valid_blk_cnt); + printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_node_count matcing with CP (de lookup) "); @@ -805,6 +933,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_node_count matcing with CP (nat lookup) "); @@ -813,6 +942,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_inode_count matched with CP "); @@ -821,8 +951,43 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } + printf("[FSCK] free segment_count matched with CP "); + if (le32_to_cpu(F2FS_CKPT(sbi)->free_segment_count) == + fsck->chk.sit_free_segs) { + printf(" [Ok..] [0x%x]\n", fsck->chk.sit_free_segs); + } else { + printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs); + ret = EXIT_ERR_CODE; + config.bug_on = 1; + } + + printf("[FSCK] next block offset is free "); + if (check_curseg_offset(sbi) == 0) { + printf(" [Ok..]\n"); + } else { + printf(" [Fail]\n"); + ret = EXIT_ERR_CODE; + config.bug_on = 1; + } + + printf("[FSCK] other corrupted bugs "); + if (config.bug_on == 0) { + printf(" [Ok..]\n"); + } else { + printf(" [Fail]\n"); + ret = EXIT_ERR_CODE; + config.bug_on = 1; + } + + /* fix global metadata */ + if (config.bug_on && config.fix_on) { + fix_nat_entries(sbi); + rewrite_sit_area_bitmap(sbi); + fix_checkpoint(sbi); + } return ret; } |