diff --git a/fsck/mount.c b/fsck/mount.c index 4c748884..13674ecc 100644 --- a/fsck/mount.c +++ b/fsck/mount.c @@ -3518,22 +3518,90 @@ static void destroy_fsync_dnodes(struct list_head *head) del_fsync_inode(entry); } +static int find_node_blk_fast(struct f2fs_sb_info *sbi, block_t *blkaddr_fast, + struct f2fs_node *node_blk_fast, bool *is_detecting) +{ + int i, err; + + for (i = 0; i < 2; i++) { + if (!f2fs_is_valid_blkaddr(sbi, *blkaddr_fast, META_POR)) { + *is_detecting = false; + return 0; + } + + err = dev_read_block(node_blk_fast, *blkaddr_fast); + if (err) + return err; + + if (!is_recoverable_dnode(sbi, node_blk_fast)) { + *is_detecting = false; + return 0; + } + + *blkaddr_fast = next_blkaddr_of_node(node_blk_fast); + } + + return 0; +} + +static int loop_node_chain_fix(block_t blkaddr_fast, + struct f2fs_node *node_blk_fast, + block_t blkaddr, struct f2fs_node *node_blk) +{ + block_t blkaddr_entry, blkaddr_tmp; + int err; + + /* find the entry point of the looped node chain */ + while (blkaddr_fast != blkaddr) { + err = dev_read_block(node_blk_fast, blkaddr_fast); + if (err) + return err; + blkaddr_fast = next_blkaddr_of_node(node_blk_fast); + + err = dev_read_block(node_blk, blkaddr); + if (err) + return err; + blkaddr = next_blkaddr_of_node(node_blk); + } + blkaddr_entry = blkaddr; + + /* find the last node of the chain */ + do { + blkaddr_tmp = blkaddr; + err = dev_read_block(node_blk, blkaddr); + if (err) + return err; + blkaddr = next_blkaddr_of_node(node_blk); + } while (blkaddr != blkaddr_entry); + + /* fix the blkaddr of last node with NULL_ADDR. */ + node_blk->footer.next_blkaddr = NULL_ADDR; + if (IS_INODE(node_blk)) + err = write_inode(node_blk, blkaddr_tmp); + else + err = dev_write_block(node_blk, blkaddr_tmp); + if (!err) + FIX_MSG("Fix looped node chain on blkaddr %u\n", + blkaddr_tmp); + return err; +} + static int find_fsync_inode(struct f2fs_sb_info *sbi, struct list_head *head) { struct curseg_info *curseg; - struct f2fs_node *node_blk; - block_t blkaddr; - unsigned int loop_cnt = 0; - unsigned int free_blocks = MAIN_SEGS(sbi) * sbi->blocks_per_seg - - sbi->total_valid_block_count; + struct f2fs_node *node_blk, *node_blk_fast; + block_t blkaddr, blkaddr_fast; + bool is_detecting = true; int err = 0; + node_blk = calloc(F2FS_BLKSIZE, 1); + node_blk_fast = calloc(F2FS_BLKSIZE, 1); + ASSERT(node_blk && node_blk_fast); + /* get node pages in the current segment */ curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); - - node_blk = calloc(F2FS_BLKSIZE, 1); - ASSERT(node_blk); + blkaddr_fast = blkaddr; while (1) { struct fsync_inode_entry *entry; @@ -3564,19 +3632,47 @@ static int find_fsync_inode(struct f2fs_sb_info *sbi, struct list_head *head) if (IS_INODE(node_blk) && is_dent_dnode(node_blk)) entry->last_dentry = blkaddr; next: - /* sanity check in order to detect looped node chain */ - if (++loop_cnt >= free_blocks || - blkaddr == next_blkaddr_of_node(node_blk)) { - MSG(0, "\tdetect looped node chain, blkaddr:%u, next:%u\n", - blkaddr, - next_blkaddr_of_node(node_blk)); + blkaddr = next_blkaddr_of_node(node_blk); + + /* Below we will detect looped node chain with Floyd's cycle + * detection algorithm. + */ + if (!is_detecting) + continue; + + err = find_node_blk_fast(sbi, &blkaddr_fast, + node_blk_fast, &is_detecting); + if (err) + break; + + if (!is_detecting) + continue; + + if (blkaddr_fast != blkaddr) + continue; + + ASSERT_MSG("\tdetect looped node chain, blkaddr:%u\n", + blkaddr); + + if (!c.fix_on) { err = -1; break; } - blkaddr = next_blkaddr_of_node(node_blk); + err = loop_node_chain_fix(NEXT_FREE_BLKADDR(sbi, curseg), + node_blk_fast, blkaddr, node_blk); + if (err) + break; + + /* Since we call get_fsync_inode() to ensure there are no + * duplicate inodes in the inode_list even if there are + * duplicate blkaddr, we can continue running after fixing the + * looped node chain. + */ + is_detecting = false; } + free(node_blk_fast); free(node_blk); return err; }