fsck.f2fs: lookup and relink root inode

As Stephanos reported in mailing list:

Info: checkpoint state = 1 :  unmount
[ASSERT] (sanity_check_nid: 362)  --> nid[0x3] ino is 0

The root cause is root inode's nat entry is corrupted, this patch
add logic to search root inode from all node blocks, try to relink
root inode's nat to target node block.

Before:

Info: checkpoint state = 185 :  trimmed nat_bits compacted_summary unmount
[lookup_nat_in_journal:3085] ==> Found nid [0x3] in nat cache
[ASSERT] (sanity_check_nat: 404)  --> nid->blk_addr is 0x0. [0x3]
Info: root inode is corrupted, search and relink it
Info: possible root inode blkaddr: 0x5a00
[lookup_nat_in_journal:3085] ==> Found nid [0x3] in nat cache
[ASSERT] (sanity_check_nat: 404)  --> nid->blk_addr is 0x0. [0x3]

[FSCK] Max image size: 94 MB, Free space: 12194 MB
[FSCK] Unreachable nat entries                        [Ok..] [0x0]
[FSCK] SIT valid block bitmap checking                [Fail]
[FSCK] Hard link checking for regular file            [Ok..] [0x0]
[FSCK] valid_block_count matching with CP             [Fail] [0x2, 0x0]
[FSCK] valid_node_count matching with CP (de lookup)  [Fail] [0x1, 0x0]
[FSCK] valid_node_count matching with CP (nat lookup) [Fail] [0x1, 0x0]
[FSCK] valid_inode_count matched with CP              [Fail] [0x1, 0x0]
[FSCK] free segment_count matched with CP             [Ok..] [0x17cd]
[FSCK] next block offset is free                      [Ok..]
[FSCK] fixing SIT types
[FSCK] other corrupted bugs                           [Fail]
Do you want to fix this partition? [Y/N] Y

After:

Info: checkpoint state = 185 :  trimmed nat_bits compacted_summary unmount
[lookup_nat_in_journal:3085] ==> Found nid [0x3] in nat cache
[ASSERT] (sanity_check_nat: 404)  --> nid->blk_addr is 0x0. [0x3]
Info: root inode is corrupted, search and relink it
Info: possible root inode blkaddr: 0x5a00
[FIX] (fsck_chk_root_inode: 730)  --> Relink root inode, blkaddr: 0x5a00
update nat(nid:3) blkaddr [0x5a00] in journal
[fsck_chk_dentry_blk:1978] [  1] Dentry Block [0x6000] Done : dentries:0 in 214 slots (len:255)

[fsck_chk_inode_blk:1244] Directory Inode: 0x3 [] depth: 1 has 0 files

[FSCK] Max image size: 94 MB, Free space: 12194 MB
[FSCK] Unreachable nat entries                        [Ok..] [0x0]
[FSCK] SIT valid block bitmap checking                [Ok..]
[FSCK] Hard link checking for regular file            [Ok..] [0x0]
[FSCK] valid_block_count matching with CP             [Ok..] [0x2]
[FSCK] valid_node_count matching with CP (de lookup)  [Ok..] [0x1]
[FSCK] valid_node_count matching with CP (nat lookup) [Ok..] [0x1]
[FSCK] valid_inode_count matched with CP              [Ok..] [0x1]
[FSCK] free segment_count matched with CP             [Ok..] [0x17cd]
[FSCK] next block offset is free                      [Ok..]
[FSCK] fixing SIT types
[FSCK] other corrupted bugs                           [Fail]
Info: flush_journal_entries() n_nats: 1, n_sits: 6
Info: Duplicate valid checkpoint to mirror position 512 -> 1024
[write_nat_bits:1737] 	Writing NAT bits pages, at offset 0x000003ff
Info: Write valid nat_bits in checkpoint
Info: write_checkpoint() cur_cp:1
[write_nat_bits:1737] 	Writing NAT bits pages, at offset 0x000003ff
Info: Write valid nat_bits in checkpoint
Info: fix_checkpoint() cur_cp:1

Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Chao Yu 2023-05-12 18:03:54 +08:00 committed by Jaegeuk Kim
parent cf3a7c30e3
commit 75f7c586c3
4 changed files with 167 additions and 7 deletions

View File

@ -386,14 +386,9 @@ err:
return -1;
}
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)
static int sanity_check_nat(struct f2fs_sb_info *sbi, u32 nid,
struct node_info *ni)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
int ret;
if (!IS_VALID_NID(sbi, nid)) {
ASSERT_MSG("nid is not valid. [0x%x]", nid);
return -EINVAL;
@ -415,6 +410,28 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid,
return -EINVAL;
}
return 0;
}
int fsck_sanity_check_nat(struct f2fs_sb_info *sbi, u32 nid)
{
struct node_info ni;
return sanity_check_nat(sbi, nid, &ni);
}
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);
int ret;
ret = sanity_check_nat(sbi, nid, ni);
if (ret)
return ret;
ret = dev_read_block(node_blk, ni->blk_addr);
ASSERT(ret >= 0);
@ -609,6 +626,123 @@ err:
return -EINVAL;
}
static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr)
{
struct seg_entry *se;
u32 offset;
se = get_seg_entry(sbi, GET_SEGNO(sbi, blk_addr));
offset = OFFSET_IN_SEG(sbi, blk_addr);
return f2fs_test_bit(offset,
(const char *)se->cur_valid_map) != 0;
}
int fsck_chk_root_inode(struct f2fs_sb_info *sbi)
{
struct f2fs_node *node_blk;
int segment_count = SM_I(sbi)->main_segments;
int segno;
bool valid_bitmap = true;
block_t last_blkaddr = NULL_ADDR;
nid_t root_ino = sbi->root_ino_num;
u64 last_ctime = 0;
u32 last_ctime_nsec = 0;
int ret = -EINVAL;
node_blk = calloc(BLOCK_SZ, 1);
ASSERT(node_blk);
MSG(0, "Info: root inode is corrupted, search and relink it\n");
retry:
for (segno = 0; segno < segment_count; segno++) {
struct seg_entry *se = get_seg_entry(sbi, segno);
block_t blkaddr = START_BLOCK(sbi, segno);
int ret;
int i;
if (IS_DATASEG(se->type))
continue;
dev_readahead(blkaddr << F2FS_BLKSIZE_BITS,
sbi->blocks_per_seg << F2FS_BLKSIZE_BITS);
for (i = 0; i < sbi->blocks_per_seg; i++, blkaddr++) {
if (valid_bitmap ^ is_sit_bitmap_set(sbi, blkaddr))
continue;
ret = dev_read_block(node_blk, blkaddr);
ASSERT(ret >= 0);
if (le32_to_cpu(node_blk->footer.ino) !=
root_ino ||
le32_to_cpu(node_blk->footer.nid) !=
root_ino)
continue;
if (!IS_INODE(node_blk))
continue;
if (le32_to_cpu(node_blk->i.i_generation) ||
le32_to_cpu(node_blk->i.i_namelen))
continue;
break;
}
if (i == sbi->blocks_per_seg)
continue;
if (valid_bitmap) {
last_blkaddr = blkaddr;
MSG(0, "Info: possible root inode blkaddr: 0x%x\n",
last_blkaddr);
goto fix;
}
if (last_blkaddr == NULL_ADDR)
goto init;
if (le64_to_cpu(node_blk->i.i_ctime) < last_ctime)
continue;
if (le64_to_cpu(node_blk->i.i_ctime) == last_ctime &&
le32_to_cpu(node_blk->i.i_ctime_nsec) <=
last_ctime_nsec)
continue;
init:
last_blkaddr = blkaddr;
last_ctime = le64_to_cpu(node_blk->i.i_ctime);
last_ctime_nsec = le32_to_cpu(node_blk->i.i_ctime_nsec);
MSG(0, "Info: possible root inode blkaddr: %u\n",
last_blkaddr);
}
if (valid_bitmap) {
valid_bitmap = false;
goto retry;
}
fix:
if (!last_blkaddr) {
MSG(0, "Info: there is no valid root inode\n");
} else if (c.fix_on) {
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
FIX_MSG("Relink root inode, blkaddr: 0x%x", last_blkaddr);
update_nat_journal_blkaddr(sbi, root_ino, last_blkaddr);
update_nat_blkaddr(sbi, root_ino, root_ino, last_blkaddr);
if (f2fs_test_bit(root_ino, fsck->nat_area_bitmap))
f2fs_clear_bit(root_ino, fsck->nat_area_bitmap);
fsck->chk.valid_nat_entry_cnt++;
if (!f2fs_test_sit_bitmap(sbi, last_blkaddr))
f2fs_set_sit_bitmap(sbi, last_blkaddr);
ret = 0;
}
free(node_blk);
return ret;
}
static inline void get_extent_info(struct extent_info *ext,
struct f2fs_extent *i_ext)
{

View File

@ -167,9 +167,11 @@ extern int fsck_chk_quota_node(struct f2fs_sb_info *);
extern int fsck_chk_quota_files(struct f2fs_sb_info *);
extern int fsck_sanity_check_nid(struct f2fs_sb_info *, u32,
enum FILE_TYPE, enum NODE_TYPE);
extern int fsck_sanity_check_nat(struct f2fs_sb_info *sbi, u32 nid);
extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
enum FILE_TYPE, enum NODE_TYPE, u32 *,
struct f2fs_compr_blk_cnt *, struct child_info *);
extern int fsck_chk_root_inode(struct f2fs_sb_info *);
extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE,
struct f2fs_node *, u32 *, struct f2fs_compr_blk_cnt *,
struct node_info *, struct child_info *);
@ -208,6 +210,8 @@ extern void update_sum_entry(struct f2fs_sb_info *, block_t,
struct f2fs_summary *);
extern void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
extern void nullify_nat_entry(struct f2fs_sb_info *, u32);
extern void update_nat_journal_blkaddr(struct f2fs_sb_info *sbi, u32 nid,
block_t blkaddr);
extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *);
extern void build_nat_area_bitmap(struct f2fs_sb_info *);
extern void build_sit_area_bitmap(struct f2fs_sb_info *);

View File

@ -885,6 +885,10 @@ static int do_fsck(struct f2fs_sb_info *sbi)
}
}
fsck_chk_orphan_node(sbi);
if (fsck_sanity_check_nat(sbi, sbi->root_ino_num))
fsck_chk_root_inode(sbi);
fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
F2FS_FT_DIR, TYPE_INODE, &blk_cnt, &cbc, NULL);
fsck_chk_quota_files(sbi);

View File

@ -3132,6 +3132,24 @@ void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid)
free(nat_block);
}
void update_nat_journal_blkaddr(struct f2fs_sb_info *sbi, u32 nid,
block_t blkaddr)
{
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_journal *journal = &curseg->sum_blk->journal;
int i;
for (i = 0; i < nats_in_cursum(journal); i++) {
if (le32_to_cpu(nid_in_journal(journal, i)) == nid) {
nat_in_journal(journal, i).block_addr =
cpu_to_le32(blkaddr);
MSG(0, "update nat(nid:%d) blkaddr [0x%x] in journal\n",
nid, blkaddr);
return;
}
}
}
void duplicate_checkpoint(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);