Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

f2fs: fix to detect recoverable inode during dryrun of find_fsync_dnodes()

mkfs.f2fs -f /dev/vdd
mount /dev/vdd /mnt/f2fs
touch /mnt/f2fs/foo
sync # avoid CP_UMOUNT_FLAG in last f2fs_checkpoint.ckpt_flags
touch /mnt/f2fs/bar
f2fs_io fsync /mnt/f2fs/bar
f2fs_io shutdown 2 /mnt/f2fs
umount /mnt/f2fs
blockdev --setro /dev/vdd
mount /dev/vdd /mnt/f2fs
mount: /mnt/f2fs: WARNING: source write-protected, mounted read-only.

For the case if we create and fsync a new inode before sudden power-cut,
without norecovery or disable_roll_forward mount option, the following
mount will succeed w/o recovering last fsynced inode.

The problem here is that we only check inode_list list after
find_fsync_dnodes() in f2fs_recover_fsync_data() to find out whether
there is recoverable data in the iamge, but there is a missed case, if
last fsynced inode is not existing in last checkpoint, then, we will
fail to get its inode due to nat of inode node is not existing in last
checkpoint, so the inode won't be linked in inode_list.

Let's detect such case in dyrun mode to fix this issue.

After this change, mount will fail as expected below:
mount: /mnt/f2fs: cannot mount /dev/vdd read-only.
dmesg(1) may have more information after failed mount system call.
demsg:
F2FS-fs (vdd): Need to recover fsync data, but write access unavailable, please try mount w/ disable_roll_forward or norecovery

Cc: stable@kernel.org
Fixes: 6781eabba1bd ("f2fs: give -EINVAL for norecovery and rw mount")
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

authored by

Chao Yu and committed by
Jaegeuk Kim
68d05693 01fba45d

+12 -8
+12 -8
fs/f2fs/recovery.c
··· 399 399 } 400 400 401 401 static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 402 - bool check_only) 402 + bool check_only, bool *new_inode) 403 403 { 404 404 struct curseg_info *curseg; 405 405 block_t blkaddr, blkaddr_fast; ··· 447 447 quota_inode = true; 448 448 } 449 449 450 - /* 451 - * CP | dnode(F) | inode(DF) 452 - * For this case, we should not give up now. 453 - */ 454 450 entry = add_fsync_inode(sbi, head, ino_of_node(folio), 455 451 quota_inode); 456 452 if (IS_ERR(entry)) { 457 453 err = PTR_ERR(entry); 458 - if (err == -ENOENT) 454 + /* 455 + * CP | dnode(F) | inode(DF) 456 + * For this case, we should not give up now. 457 + */ 458 + if (err == -ENOENT) { 459 + if (check_only) 460 + *new_inode = true; 459 461 goto next; 462 + } 460 463 f2fs_folio_put(folio, true); 461 464 break; 462 465 } ··· 878 875 int ret = 0; 879 876 unsigned long s_flags = sbi->sb->s_flags; 880 877 bool need_writecp = false; 878 + bool new_inode = false; 881 879 882 880 f2fs_notice(sbi, "f2fs_recover_fsync_data: recovery fsync data, " 883 881 "check_only: %d", check_only); ··· 894 890 f2fs_down_write(&sbi->cp_global_sem); 895 891 896 892 /* step #1: find fsynced inode numbers */ 897 - err = find_fsync_dnodes(sbi, &inode_list, check_only); 898 - if (err || list_empty(&inode_list)) 893 + err = find_fsync_dnodes(sbi, &inode_list, check_only, &new_inode); 894 + if (err < 0 || (list_empty(&inode_list) && (!check_only || !new_inode))) 899 895 goto skip; 900 896 901 897 if (check_only) {