link count works differently in bcachefs - it's only nonzero for files with multiple hardlinks, which means we can also avoid checking it except for files that are known to have hardlinks. That means we need a few different checks instead; in particular, we don't want fsck to delet a file that has a dirent pointing to it.
Unified · Split
| @@ -1029,6 +1029,7 @@ static int check_inode(struct btree_trans *trans, · 1028 unmodified lines | ||
| 1029 | 1029 | bool full) |
| 1030 | 1030 | { |
| 1031 | 1031 | struct bch_fs *c = trans->c; |
| 1032 | + struct printbuf buf = PRINTBUF; | |
| 1032 | 1033 | struct bch_inode_unpacked u; |
| 1033 | 1034 | bool do_update = false; |
| 1034 | 1035 | int ret; |
| @@ -1062,7 +1063,24 @@ static int check_inode(struct btree_trans *trans, · 27 unmodified lines | ||
| 1062 | 1063 | trans, inode_snapshot_mismatch, |
| 1063 | 1064 | "inodes in different snapshots don't match")) { |
| 1064 | 1065 | bch_err(c, "repair not implemented yet"); |
| 1065 | − return -BCH_ERR_fsck_repair_unimplemented; | |
| 1066 | + ret = -BCH_ERR_fsck_repair_unimplemented; | |
| 1067 | + goto err_noprint; | |
| 1068 | + } | |
| 1069 | + | |
| 1070 | + if (u.bi_dir || u.bi_dir_offset) { | |
| 1071 | + ret = check_inode_dirent_inode(trans, &u, &do_update); | |
| 1072 | + if (ret) | |
| 1073 | + goto err; | |
| 1074 | + } | |
| 1075 | + | |
| 1076 | + if (fsck_err_on(u.bi_dir && (u.bi_flags & BCH_INODE_unlinked), | |
| 1077 | + trans, inode_unlinked_but_has_dirent, | |
| 1078 | + "inode unlinked but has dirent\n%s", | |
| 1079 | + (printbuf_reset(&buf), | |
| 1080 | + bch2_inode_unpacked_to_text(&buf, &u), | |
| 1081 | + buf.buf))) { | |
| 1082 | + u.bi_flags &= ~BCH_INODE_unlinked; | |
| 1083 | + do_update = true; | |
| 1066 | 1084 | } |
| 1067 | 1085 | |
| 1068 | 1086 | if ((u.bi_flags & (BCH_INODE_i_size_dirty|BCH_INODE_unlinked)) && |
| @@ -1079,11 +1097,11 @@ static int check_inode(struct btree_trans *trans, · 10 unmodified lines | ||
| 1079 | 1097 | |
| 1080 | 1098 | bch_err_msg(c, ret, "in fsck updating inode"); |
| 1081 | 1099 | if (ret) |
| 1082 | − return ret; | |
| 1100 | + goto err_noprint; | |
| 1083 | 1101 | |
| 1084 | 1102 | if (!bpos_eq(new_min_pos, POS_MIN)) |
| 1085 | 1103 | bch2_btree_iter_set_pos(iter, bpos_predecessor(new_min_pos)); |
| 1086 | − return 0; | |
| 1104 | + goto err_noprint; | |
| 1087 | 1105 | } |
| 1088 | 1106 | |
| 1089 | 1107 | if (u.bi_flags & BCH_INODE_unlinked) { |
| @@ -1100,7 +1118,7 @@ static int check_inode(struct btree_trans *trans, · 10 unmodified lines | ||
| 1100 | 1118 | */ |
| 1101 | 1119 | ret = check_inode_deleted_list(trans, k.k->p); |
| 1102 | 1120 | if (ret < 0) |
| 1103 | − return ret; | |
| 1121 | + goto err_noprint; | |
| 1104 | 1122 | |
| 1105 | 1123 | fsck_err_on(!ret, |
| 1106 | 1124 | trans, unlinked_inode_not_on_deleted_list, |
| @@ -1117,7 +1135,7 @@ static int check_inode(struct btree_trans *trans, · 10 unmodified lines | ||
| 1117 | 1135 | u.bi_inum, u.bi_snapshot)) { |
| 1118 | 1136 | ret = bch2_inode_rm_snapshot(trans, u.bi_inum, iter->pos.snapshot); |
| 1119 | 1137 | bch_err_msg(c, ret, "in fsck deleting inode"); |
| 1120 | − return ret; | |
| 1138 | + goto err_noprint; | |
| 1121 | 1139 | } |
| 1122 | 1140 | } |
| 1123 | 1141 | } |
| @@ -1182,12 +1200,6 @@ static int check_inode(struct btree_trans *trans, · 58 unmodified lines | ||
| 1182 | 1200 | do_update = true; |
| 1183 | 1201 | } |
| 1184 | 1202 | |
| 1185 | − if (u.bi_dir || u.bi_dir_offset) { | |
| 1186 | − ret = check_inode_dirent_inode(trans, &u, &do_update); | |
| 1187 | − if (ret) | |
| 1188 | − goto err; | |
| 1189 | − } | |
| 1190 | − | |
| 1191 | 1203 | if (fsck_err_on(u.bi_parent_subvol && |
| 1192 | 1204 | (u.bi_subvol == 0 || |
| 1193 | 1205 | u.bi_subvol == BCACHEFS_ROOT_SUBVOL), |
| @@ -1232,11 +1244,13 @@ do_update: · 38 unmodified lines | ||
| 1232 | 1244 | ret = __bch2_fsck_write_inode(trans, &u, iter->pos.snapshot); |
| 1233 | 1245 | bch_err_msg(c, ret, "in fsck updating inode"); |
| 1234 | 1246 | if (ret) |
| 1235 | − return ret; | |
| 1247 | + goto err_noprint; | |
| 1236 | 1248 | } |
| 1237 | 1249 | err: |
| 1238 | 1250 | fsck_err: |
| 1239 | 1251 | bch_err_fn(c, ret); |
| 1252 | +err_noprint: | |
| 1253 | + printbuf_exit(&buf); | |
| 1240 | 1254 | return ret; |
| 1241 | 1255 | } |
| 1242 | 1256 | |
| @@ -1831,11 +1845,22 @@ static int check_dirent_inode_dirent(struct btree_trans *trans, · 588 unmodified lines | ||
| 1831 | 1845 | fsck_err_on(S_ISDIR(target->bi_mode), |
| 1832 | 1846 | trans, inode_dir_missing_backpointer, |
| 1833 | 1847 | "directory with missing backpointer\n%s", |
| 1834 | − (bch2_bkey_val_to_text(&buf, c, d.s_c), | |
| 1835 | − prt_printf(&buf, "\n "), | |
| 1848 | + (printbuf_reset(&buf), | |
| 1849 | + bch2_bkey_val_to_text(&buf, c, d.s_c), | |
| 1850 | + prt_printf(&buf, "\n"), | |
| 1836 | 1851 | bch2_inode_unpacked_to_text(&buf, target), |
| 1837 | 1852 | buf.buf)); |
| 1838 | 1853 | |
| 1854 | + fsck_err_on(target->bi_flags & BCH_INODE_unlinked, | |
| 1855 | + trans, inode_unlinked_but_has_dirent, | |
| 1856 | + "inode unlinked but has dirent\n%s", | |
| 1857 | + (printbuf_reset(&buf), | |
| 1858 | + bch2_bkey_val_to_text(&buf, c, d.s_c), | |
| 1859 | + prt_printf(&buf, "\n"), | |
| 1860 | + bch2_inode_unpacked_to_text(&buf, target), | |
| 1861 | + buf.buf)); | |
| 1862 | + | |
| 1863 | + target->bi_flags &= ~BCH_INODE_unlinked; | |
| 1839 | 1864 | target->bi_dir = d.k->p.inode; |
| 1840 | 1865 | target->bi_dir_offset = d.k->p.offset; |
| 1841 | 1866 | return __bch2_fsck_write_inode(trans, target, target_snapshot); |
| @@ -211,6 +211,7 @@ enum bch_fsck_flags { · 210 unmodified lines | ||
| 211 | 211 | x(inode_unlinked_but_clean, 197, 0) \ |
| 212 | 212 | x(inode_unlinked_but_nlink_nonzero, 198, 0) \ |
| 213 | 213 | x(inode_unlinked_and_not_open, 281, 0) \ |
| 214 | + x(inode_unlinked_but_has_dirent, 285, 0) \ | |
| 214 | 215 | x(inode_checksum_type_invalid, 199, 0) \ |
| 215 | 216 | x(inode_compression_type_invalid, 200, 0) \ |
| 216 | 217 | x(inode_subvol_root_but_not_dir, 201, 0) \ |
| @@ -296,7 +297,7 @@ enum bch_fsck_flags { · 79 unmodified lines | ||
| 296 | 297 | x(accounting_key_replicas_devs_unsorted, 280, FSCK_AUTOFIX) \ |
| 297 | 298 | x(accounting_key_version_0, 282, FSCK_AUTOFIX) \ |
| 298 | 299 | x(logged_op_but_clean, 283, FSCK_AUTOFIX) \ |
| 299 | − x(MAX, 285, 0) | |
| 300 | + x(MAX, 286, 0) | |
| 300 | 301 | |
| 301 | 302 | enum bch_sb_error_id { |
| 302 | 303 | #define x(t, n, ...) BCH_FSCK_ERR_##t = n, |