diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2025-06-30 17:07:31 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2025-06-30 18:30:55 -0400 |
commit | 67d67ff15b357d376fc0775d0add3573d42e80a9 (patch) | |
tree | cb768e6a0b13a5f1e88ab8cf60e7b404e55437fe | |
parent | 5a1be50715c84f91937c3940afd41e7feaa8698f (diff) |
bcachefs: Before removing dangling dirents, check for contents
If we find a dirent pointing to a missing inode, check for
dirents/extents assocatiated with that inode number: if present,
reconstruct the inode insead of deleting the dirent.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | fs/bcachefs/fsck.c | 45 | ||||
-rw-r--r-- | fs/bcachefs/sb-errors_format.h | 3 |
2 files changed, 47 insertions, 1 deletions
diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 62756f465e56..1a4080ac66f7 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -1572,6 +1572,44 @@ reconstruct: goto out; } +static int maybe_reconstruct_inum_btree(struct btree_trans *trans, + u64 inum, u32 snapshot, + enum btree_id btree) +{ + struct btree_iter iter; + struct bkey_s_c k; + int ret = 0; + + for_each_btree_key_max_norestart(trans, iter, btree, + SPOS(inum, 0, snapshot), + POS(inum, U64_MAX), + 0, k, ret) { + ret = 1; + break; + } + bch2_trans_iter_exit(trans, &iter); + + if (ret <= 0) + return ret; + + if (fsck_err(trans, missing_inode_with_contents, + "inode %llu:%u type %s missing, but contents found: reconstruct?", + inum, snapshot, + btree == BTREE_ID_extents ? "reg" : "dir")) + return reconstruct_inode(trans, btree, snapshot, inum) ?: + bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?: + bch_err_throw(trans->c, transaction_restart_commit); +fsck_err: + return ret; +} + +static int maybe_reconstruct_inum(struct btree_trans *trans, + u64 inum, u32 snapshot) +{ + return maybe_reconstruct_inum_btree(trans, inum, snapshot, BTREE_ID_extents) ?: + maybe_reconstruct_inum_btree(trans, inum, snapshot, BTREE_ID_dirents); +} + static int check_i_sectors_notnested(struct btree_trans *trans, struct inode_walker *w) { struct bch_fs *c = trans->c; @@ -2366,6 +2404,13 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter, if (ret) goto err; + if (!target->inodes.nr) { + ret = maybe_reconstruct_inum(trans, le64_to_cpu(d.v->d_inum), + d.k->p.snapshot); + if (ret) + return ret; + } + if (fsck_err_on(!target->inodes.nr, trans, dirent_to_missing_inode, "dirent points to missing inode:\n%s", diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h index 3ecac2524118..02605976a114 100644 --- a/fs/bcachefs/sb-errors_format.h +++ b/fs/bcachefs/sb-errors_format.h @@ -291,6 +291,7 @@ enum bch_fsck_flags { x(inode_points_to_missing_dirent, 249, FSCK_AUTOFIX) \ x(inode_points_to_wrong_dirent, 250, FSCK_AUTOFIX) \ x(inode_bi_parent_nonzero, 251, 0) \ + x(missing_inode_with_contents, 321, FSCK_AUTOFIX) \ x(dirent_to_missing_parent_subvol, 252, 0) \ x(dirent_not_visible_in_parent_subvol, 253, 0) \ x(subvol_fs_path_parent_wrong, 254, 0) \ @@ -332,7 +333,7 @@ enum bch_fsck_flags { x(dirent_stray_data_after_cf_name, 305, 0) \ x(rebalance_work_incorrectly_set, 309, FSCK_AUTOFIX) \ x(rebalance_work_incorrectly_unset, 310, FSCK_AUTOFIX) \ - x(MAX, 321, 0) + x(MAX, 322, 0) enum bch_sb_error_id { #define x(t, n, ...) BCH_FSCK_ERR_##t = n, |