commit 18076aac0276719f1476f5371d942328a6483a1b (HEAD -> master) Author: Luke Dashjr Date: Sat Aug 25 02:25:31 2018 +0000 rebase rebuild-root.diff diff --git a/fsck/fsck.c b/fsck/fsck.c index 05a6301..08b117b 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -391,6 +391,8 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, ASSERT_MSG("nid[0x%x] ino is 0", nid); return -EINVAL; } + if (nid == 3) + printf("DEBUG: %s: root inode blk_addr %#x\n", __func__, ni->blk_addr); if (ni->blk_addr == NEW_ADDR) { ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid); @@ -563,6 +565,457 @@ err: return -EINVAL; } +struct wrapper { + union { + /* + * direct/indirect/double-indirect and xattr + * offs in node_footer->flags is used to sort nodes + */ + struct f2fs_node *node; + /* FIXME: data block should be all dentries */ + struct data { + struct f2fs_dentry_block *dentry; + /* sum_entry->ofs_in_node is used to sort data */ + struct f2fs_summary *sum; + } data; + }; + block_t blk_addr; + struct wrapper *next; +}; +struct wrapper *node_head = NULL, *data_head = NULL, *xattr_head = NULL; +/* + * if nat entry of root inode is corrupted, we may find correct root inode + * block during scanning. So we save them + */ +struct wrapper *root_head = NULL; + +static void add_wrapper(struct wrapper **head, struct wrapper *new) +{ + if (*head == NULL) { + *head = new; + } else { + new->next = *head; + *head = new; + } +} + +static int get_all_wrappers(struct f2fs_sb_info *sbi) +{ + struct seg_entry *se; + struct f2fs_dentry_block *dentry_blk; + struct f2fs_node *node_blk; + struct f2fs_summary_block *sum_blk; + struct f2fs_summary *sum_entry; + u32 seg_idx, sum_idx; + int sum_type, ret; + nid_t root_ino = F2FS_ROOT_INO(sbi); + struct wrapper *new; + + for (seg_idx = 0; seg_idx < TOTAL_SEGS(sbi); seg_idx++) { + se = get_seg_entry(sbi, seg_idx); + if (!se->valid_blocks) + /* skip unused segment */ + continue; + + sum_blk = get_sum_block(sbi, seg_idx, &sum_type); + if (!sum_blk) + return -ENOMEM; + + for (sum_idx = 0; sum_idx < ENTRIES_IN_SUM; sum_idx++) { + block_t blkaddr = SM_I(sbi)->main_blkaddr + + seg_idx * sbi->blocks_per_seg + + sum_idx; + + if (!f2fs_test_sit_bitmap(sbi, blkaddr)) + /* skip unused block */ + continue; + + sum_entry = &sum_blk->entries[sum_idx]; + if (le32_to_cpu(sum_entry->nid) != root_ino) + /* skip block whose parent nid is not root ino */ + continue; + + new = (struct wrapper *)calloc(sizeof(struct wrapper), 1); + ASSERT(new); + + if (sum_type == SEG_TYPE_NODE || sum_type == SEG_TYPE_CUR_NODE) { + node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); + ASSERT(node_blk); + + ret = dev_read_block(node_blk, blkaddr); + ASSERT(ret >= 0); + if (le32_to_cpu(node_blk->footer.ino) == le32_to_cpu(node_blk->footer.nid)) { + /* skip the root inode itself */ + if (le32_to_cpu(node_blk->footer.ino) == root_ino) { + DBG(1, "add block %#x to root_head\n", blkaddr); + new->node = node_blk; + new->blk_addr = blkaddr; + new->next = NULL; + add_wrapper(&root_head, new); + continue; + } + } + /* FIXME: shall we do some check here? if it + * does not pass, we should drop the data + * block. + * Or, we don't need to check this, fsck + * will do this later + */ + new->node = node_blk; + new->blk_addr = blkaddr; + new->next = NULL; + if (ofs_of_node(node_blk) == XATTR_NODE_OFFSET) + add_wrapper(&xattr_head, new); + else + add_wrapper(&node_head, new); + } else if (sum_type == SEG_TYPE_DATA || sum_type == SEG_TYPE_CUR_DATA) { + dentry_blk = (struct f2fs_dentry_block *)calloc(BLOCK_SZ, 1); + ASSERT(dentry_blk); + + ret = dev_read_block(dentry_blk, blkaddr); + ASSERT(ret >= 0); + /* FIXME: shall we do some check here? if it + * does not pass, we should drop the data + * block. + * Or, we don't need to check this, fsck + * will do this later + */ + new->data.dentry = dentry_blk; + new->data.sum = calloc(sizeof(struct f2fs_summary), 1); + memcpy(new->data.sum, sum_entry, sizeof(struct f2fs_summary)); + new->blk_addr = blkaddr; + new->next = NULL; + add_wrapper(&data_head, new); + } else { + ASSERT_MSG("segment 0x%x: unknown summary type %d\n", + seg_idx, sum_type); + break; + } + } + if (sum_type == SEG_TYPE_NODE || + sum_type == SEG_TYPE_DATA || + sum_type == SEG_TYPE_MAX) + free(sum_blk); + } + return 0; +} + +static int update_nat_entry(struct f2fs_sb_info *sbi, u32 nid, block_t newblk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct f2fs_nat_block *nat_block; + struct f2fs_nat_entry *ne; + pgoff_t block_off; + pgoff_t nat_blk_addr; + block_t wrong_addr; + int seg_off, entry_off; + int i, ret; + + /* look up nat entry in journal */ + for (i = 0; i < nats_in_cursum((&sum->journal)); i++) { + if (le32_to_cpu(nid_in_journal((&sum->journal), i)) == nid) { + ne = &nat_in_journal((&sum->journal), i); + wrong_addr = le32_to_cpu(ne->block_addr); + ne->block_addr = cpu_to_le32(newblk); + goto out; + } + } + + /* look up nat entry in NAT area */ + nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); + ASSERT(nat_block); + + block_off = nid / NAT_ENTRY_PER_BLOCK; + entry_off = nid % NAT_ENTRY_PER_BLOCK; + + seg_off = block_off >> sbi->log_blocks_per_seg; + nat_blk_addr = (pgoff_t)(nm_i->nat_blkaddr + + (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + + if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) + nat_blk_addr += sbi->blocks_per_seg; + + ret = dev_read_block(nat_block, nat_blk_addr); + ASSERT(ret >= 0); + + ne = &nat_block->entries[entry_off]; + wrong_addr = le32_to_cpu(ne->block_addr); + ne->block_addr = cpu_to_le32(newblk); + + dev_write_block(nat_block, nat_blk_addr); + +out: + f2fs_set_bit(nid, fsck->nat_area_bitmap); + FIX_MSG("nat entry [%d] block_addr [0x%x] -> [0x%x]\n", + i, wrong_addr, newblk); + return 0; +} + +static int update_sit_entry(struct f2fs_sb_info *sbi, block_t blk_addr) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct seg_entry *se; + + if (f2fs_test_sit_bitmap(sbi, blk_addr)) { + se = get_seg_entry(sbi, GET_SEGNO(sbi, blk_addr)); + se->valid_blocks += 1; + f2fs_set_bit(OFFSET_IN_SEG(sbi, blk_addr), (char *)se->cur_valid_map); + f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->sit_area_bitmap); + } + + return 0; +} + +static int do_fix_root_inode(struct f2fs_sb_info *sbi, + struct f2fs_node *root_node) +{ + struct node_info ni; + u32 noffset; + u64 cnt; + struct wrapper *new, *ptr = NULL; + struct f2fs_inode *root_inode; + block_t blkaddr; + /* shall we truct that the inode block really is root inode block */ + int weight = 0, pre_weight = 0; + int fixed = 0; + nid_t root_ino = F2FS_ROOT_INO(sbi); + int ret; + + /* get inode block */ + ASSERT(root_node); + new = (struct wrapper *)calloc(sizeof(struct wrapper), 1); + get_node_info(sbi, root_ino, &ni); + new->node = root_node; + new->blk_addr = ni.blk_addr; + new->next = NULL; + add_wrapper(&root_head, new); + + /* + * check if the inode block is really the root inode block, choose + * the node block which has the largest weight + */ + while (root_head) { + if (le32_to_cpu(root_head->node->footer.ino) == root_ino) + weight++; + if (le32_to_cpu(root_head->node->footer.nid) == root_ino) + weight++; + if (S_ISDIR(le16_to_cpu(root_head->node->i.i_mode))) + weight++; + /* FIXME: more weight? */ + + if (weight > pre_weight) { + /* FIXME: then how about >=? */ + new = root_head; + pre_weight = weight; + DBG(1, "find new root_node block %#x\n", new->blk_addr); + root_head = root_head->next; + continue; + } + ptr = root_head; + root_head = root_head->next; + free(ptr->node); + free(ptr); + } + if (pre_weight < 2) { + /* + * nat entry may be corrupted, we should not trust what we + * read. We should select a new place to write it back + */ + blkaddr = 0; //FIXME: find a new position + } else { + blkaddr = new->blk_addr; + root_node = new->node; + } + if (new->blk_addr != ni.blk_addr || root_node != new->node) { + ret = update_nat_entry(sbi, root_ino, new->blk_addr); + ASSERT(ret == 0); + ret = update_sit_entry(sbi, new->blk_addr); + ASSERT(ret == 0); + } + + root_inode = &root_node->i; + /* recover i_addr[] */ + cnt = 0; + while (data_head) { + ASSERT(cnt < DEF_ADDRS_PER_INODE); + noffset = le32_to_cpu(data_head->data.sum->ofs_in_node); + if (le32_to_cpu(root_inode->i_addr[noffset]) != data_head->blk_addr) { + ASSERT_MSG("root inode: i_addr[%u] = 0x%x, 0x%x\n", + noffset, le32_to_cpu(root_inode->i_addr[noffset]), + data_head->blk_addr); + root_inode->i_addr[noffset] = cpu_to_le32(data_head->blk_addr); + FIX_MSG("set i_addr[%u] = 0x%x\n", noffset, data_head->blk_addr); + fixed = 1; + } + ptr = data_head; + data_head = data_head->next; + free(ptr->data.dentry); + free(ptr->data.sum); + free(ptr); + cnt++; + } + DBG(1, "root inode: data blocks %lu\n", cnt); + + /* recover i_nid[] */ + cnt = 0; + while (node_head) { + ASSERT(cnt < 5); + noffset = ofs_of_node(node_head->node); + /* check if noffset is valid */ + if (noffset == 1 || noffset == 2 || noffset == 3) { + /* noffset is valid, calc the index in i_nid */ + noffset = noffset - 1; + } else if (noffset == 4 + ADDRS_PER_BLOCK) { + /* i_nid[3] */ + noffset = 3; + } else if (noffset == 4 + ADDRS_PER_BLOCK + NIDS_PER_BLOCK) { + /* i_nid[4] */ + noffset = 4; + } else { + /* noffset = 0, node is in i_addr, this must be wrong. + * noffset = other value, node is in indirect/didirect + * node. We drop these nodes. + */ + ASSERT_MSG("root inode: offset of node 0x%x\n", noffset); + FIX_MSG(" drop node block 0x%x\n", node_head->blk_addr); + fixed = 1; + break; + } + if (root_inode->i_nid[noffset] != node_head->node->footer.nid) { + ASSERT_MSG("root inode: i_nid[%u] = 0x%x, 0x%x\n", + noffset, le32_to_cpu(root_inode->i_nid[noffset]), + le32_to_cpu(node_head->node->footer.nid)); + root_inode->i_nid[noffset] = node_head->node->footer.nid; + FIX_MSG("set i_nid[%u] = 0x%x\n", noffset, + le32_to_cpu(node_head->node->footer.nid)); + fixed = 1; + } + ptr = node_head; + node_head = node_head->next; + free(ptr->node); + free(ptr); + cnt++; + } + DBG(1, "root inode: node blocks %lu\n", cnt); + + /* recover i_xattr_nid */ + cnt = 0; + while (xattr_head) { + ASSERT(cnt < 1); + if (root_inode->i_xattr_nid != xattr_head->node->footer.nid) { + root_inode->i_xattr_nid = xattr_head->node->footer.nid; + FIX_MSG("set i_xattr_nid = 0x%x\n", + le32_to_cpu(xattr_head->node->footer.nid)); + fixed = 1; + } + ptr = xattr_head; + xattr_head = xattr_head->next; + free(ptr->node); + free(ptr); + cnt++; + } + DBG(1, "root inode: xattr blocks %lu\n", cnt); + + /* recover other meta of root inode */ + if (le16_to_cpu(root_inode->i_mode) != 0x41ed) { + root_inode->i_mode = cpu_to_le16(0x41ed); // from f2fs_write_root_inode + FIX_MSG("set i_mode = 0x41ed\n"); + fixed = 1; + } + /* FIXME: how to set appropriate values to these 2 level */ + if (le32_to_cpu(root_inode->i_current_depth) > MAX_DIR_HASH_DEPTH) { + root_inode->i_current_depth = cpu_to_le32(MAX_DIR_HASH_DEPTH); + FIX_MSG("set i_current_depth = %u\n", MAX_DIR_HASH_DEPTH); + fixed = 1; + } + if (le32_to_cpu(root_inode->i_dir_level) > MAX_DIR_HASH_DEPTH) { + root_inode->i_dir_level = cpu_to_le32(DEF_DIR_LEVEL); + FIX_MSG("set i_dir_level = %u\n", DEF_DIR_LEVEL); + fixed = 1; + } + /* FIXME: is root_inode->i_pino 3? why I get 0 in my case? */ +// if (le32_to_cpu(root_inode->i_pino) != root_ino) { +// root_inode->i_pino = cpu_to_le32(root_ino); +// FIX_MSG("set i_pino = %u\n", root_ino); +// fixed = 1; +// } + /* + * XXX: i_advise, i_inline, i_uid, i_gid, i_*time, i_*time_nsec, + * i_generation, i_flagsi_flags, i_namelen, i_name + * + * XXX: the later fsck -f will fix: i_links, i_blocks, i_ext + */ + + if (fixed && !c.ro) { + ret = dev_write_block(root_node, blkaddr); + ASSERT(ret >= 0); + } + + return fixed; +} + +/* + * we can only recover root inode by scanning SSA. Because inode may + * contains data, only SSA saves which nid the block belonging to + */ +static int fix_root_inode(struct f2fs_sb_info *sbi, struct f2fs_node *root_node) +{ + int ret; + + /* 1. get all node/data blocks of root inode */ + ret = get_all_wrappers(sbi); + if (ret) + return ret; + + /* 2. recover root inode and write it back */ + ret = do_fix_root_inode(sbi, root_node); + if (ret) + FIX_MSG("fixed root inode\n"); + + return 0; +} + +int fsck_chk_fs(struct f2fs_sb_info *sbi, u32 *blk_cnt) +{ + struct node_info ni; + struct f2fs_node *node_blk = NULL; + struct f2fs_inode *root_inode; + enum FILE_TYPE ftype = F2FS_FT_DIR; + enum NODE_TYPE ntype = TYPE_INODE; + u32 ino = F2FS_ROOT_INO(sbi); + int retries = 0, ret; + + node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); + ASSERT(node_blk != NULL); + +retry: + /* check if F2FS_ROOT_INO is a correct inode */ + ret = sanity_check_nid(sbi, ino, node_blk, ftype, ntype, &ni); + if (ret) + goto fix; + + root_inode = &node_blk->i; + + /* check if F2FS_ROOT_INO is a directory */ + if (!S_ISDIR(le16_to_cpu(root_inode->i_mode))) { + ASSERT_MSG("Root inode is not a directory\n"); + goto fix; + } + + fsck_chk_inode_blk(sbi, ino, ftype, node_blk, blk_cnt, &ni, NULL); + return 0; +fix: + ret = fix_root_inode(sbi, node_blk); + retries++; + ASSERT(ret == 0); + ASSERT(retries < 2); // FIXME: how many times to retry? + goto retry; +} + static inline void get_extent_info(struct extent_info *ext, struct f2fs_extent *i_ext) { @@ -1410,6 +1863,8 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child, } i++; + print_dentry(fsck->dentry_depth, name, bitmap, + dentry, max, i, last_blk, enc_name); free(name); continue; } diff --git a/fsck/fsck.h b/fsck/fsck.h index 8e133fa..2267492 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -125,6 +125,7 @@ struct selabel_handle; extern int fsck_chk_orphan_node(struct f2fs_sb_info *); extern int fsck_chk_quota_node(struct f2fs_sb_info *); extern int fsck_chk_quota_files(struct f2fs_sb_info *); +extern int fsck_chk_fs(struct f2fs_sb_info *sbi, u32 *blk_cnt); extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32, enum FILE_TYPE, enum NODE_TYPE, u32 *, struct child_info *); diff --git a/fsck/main.c b/fsck/main.c index bbf82c3..97a0d9d 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -562,8 +562,7 @@ static void do_fsck(struct f2fs_sb_info *sbi) } } fsck_chk_orphan_node(sbi); - fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, - F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL); + fsck_chk_fs(sbi, &blk_cnt); fsck_chk_quota_files(sbi); fsck_verify(sbi); diff --git a/fsck/mount.c b/fsck/mount.c index 4a14320..dd874e5 100644 --- a/fsck/mount.c +++ b/fsck/mount.c @@ -245,10 +245,11 @@ void print_inode_info(struct f2fs_sb_info *sbi, DISP_u32(inode, i_addr[ofs + 3]); /* Pointers to data blocks */ for (i = ofs + 3; i < ADDRS_PER_INODE(inode); i++) { - if (inode->i_addr[i] == 0x0) - break; - printf("i_addr[0x%x] points data block\t\t[0x%4x]\n", - i, le32_to_cpu(inode->i_addr[i])); +// if (inode->i_addr[i] == 0x0) +// break; +// printf("i_addr[0x%x] points data block\t\t[0x%4x]\n", +// i, le32_to_cpu(inode->i_addr[i])); + DISP_u32(inode, i_addr[i]); /* Pointers to data blocks */ } DISP_u32(inode, i_nid[0]); /* direct */