Skip to content

Commit c7da5ee

Browse files
author
Kent Overstreet
committed
bcachefs: Check for unlinked inodes with dirents
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. Signed-off-by: Kent Overstreet <[email protected]>
1 parent 1c6051b commit c7da5ee

File tree

2 files changed

+41
-15
lines changed

2 files changed

+41
-15
lines changed

fs/bcachefs/fsck.c

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ static int check_inode(struct btree_trans *trans,
10291029
bool full)
10301030
{
10311031
struct bch_fs *c = trans->c;
1032+
struct printbuf buf = PRINTBUF;
10321033
struct bch_inode_unpacked u;
10331034
bool do_update = false;
10341035
int ret;
@@ -1062,7 +1063,24 @@ static int check_inode(struct btree_trans *trans,
10621063
trans, inode_snapshot_mismatch,
10631064
"inodes in different snapshots don't match")) {
10641065
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;
10661084
}
10671085

10681086
if ((u.bi_flags & (BCH_INODE_i_size_dirty|BCH_INODE_unlinked)) &&
@@ -1079,11 +1097,11 @@ static int check_inode(struct btree_trans *trans,
10791097

10801098
bch_err_msg(c, ret, "in fsck updating inode");
10811099
if (ret)
1082-
return ret;
1100+
goto err_noprint;
10831101

10841102
if (!bpos_eq(new_min_pos, POS_MIN))
10851103
bch2_btree_iter_set_pos(iter, bpos_predecessor(new_min_pos));
1086-
return 0;
1104+
goto err_noprint;
10871105
}
10881106

10891107
if (u.bi_flags & BCH_INODE_unlinked) {
@@ -1100,7 +1118,7 @@ static int check_inode(struct btree_trans *trans,
11001118
*/
11011119
ret = check_inode_deleted_list(trans, k.k->p);
11021120
if (ret < 0)
1103-
return ret;
1121+
goto err_noprint;
11041122

11051123
fsck_err_on(!ret,
11061124
trans, unlinked_inode_not_on_deleted_list,
@@ -1117,7 +1135,7 @@ static int check_inode(struct btree_trans *trans,
11171135
u.bi_inum, u.bi_snapshot)) {
11181136
ret = bch2_inode_rm_snapshot(trans, u.bi_inum, iter->pos.snapshot);
11191137
bch_err_msg(c, ret, "in fsck deleting inode");
1120-
return ret;
1138+
goto err_noprint;
11211139
}
11221140
}
11231141
}
@@ -1182,12 +1200,6 @@ static int check_inode(struct btree_trans *trans,
11821200
do_update = true;
11831201
}
11841202

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-
11911203
if (fsck_err_on(u.bi_parent_subvol &&
11921204
(u.bi_subvol == 0 ||
11931205
u.bi_subvol == BCACHEFS_ROOT_SUBVOL),
@@ -1232,11 +1244,13 @@ static int check_inode(struct btree_trans *trans,
12321244
ret = __bch2_fsck_write_inode(trans, &u, iter->pos.snapshot);
12331245
bch_err_msg(c, ret, "in fsck updating inode");
12341246
if (ret)
1235-
return ret;
1247+
goto err_noprint;
12361248
}
12371249
err:
12381250
fsck_err:
12391251
bch_err_fn(c, ret);
1252+
err_noprint:
1253+
printbuf_exit(&buf);
12401254
return ret;
12411255
}
12421256

@@ -1831,11 +1845,22 @@ static int check_dirent_inode_dirent(struct btree_trans *trans,
18311845
fsck_err_on(S_ISDIR(target->bi_mode),
18321846
trans, inode_dir_missing_backpointer,
18331847
"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"),
18361851
bch2_inode_unpacked_to_text(&buf, target),
18371852
buf.buf));
18381853

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;
18391864
target->bi_dir = d.k->p.inode;
18401865
target->bi_dir_offset = d.k->p.offset;
18411866
return __bch2_fsck_write_inode(trans, target, target_snapshot);

fs/bcachefs/sb-errors_format.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ enum bch_fsck_flags {
211211
x(inode_unlinked_but_clean, 197, 0) \
212212
x(inode_unlinked_but_nlink_nonzero, 198, 0) \
213213
x(inode_unlinked_and_not_open, 281, 0) \
214+
x(inode_unlinked_but_has_dirent, 285, 0) \
214215
x(inode_checksum_type_invalid, 199, 0) \
215216
x(inode_compression_type_invalid, 200, 0) \
216217
x(inode_subvol_root_but_not_dir, 201, 0) \
@@ -296,7 +297,7 @@ enum bch_fsck_flags {
296297
x(accounting_key_replicas_devs_unsorted, 280, FSCK_AUTOFIX) \
297298
x(accounting_key_version_0, 282, FSCK_AUTOFIX) \
298299
x(logged_op_but_clean, 283, FSCK_AUTOFIX) \
299-
x(MAX, 285, 0)
300+
x(MAX, 286, 0)
300301

301302
enum bch_sb_error_id {
302303
#define x(t, n, ...) BCH_FSCK_ERR_##t = n,

0 commit comments

Comments
 (0)