From c72def523799a0b054fd7cbbed32509e365db55b Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sat, 31 May 2025 19:12:25 -0400 Subject: [PATCH] bcachefs: Run check_dirents second time if required If we move a key backwards, we'll need a second pass to run the rest of the fsck checks. Signed-off-by: Kent Overstreet --- fs/bcachefs/dirent.c | 3 ++- fs/bcachefs/fsck.c | 39 ++++++++++++++++++++++++++------------- fs/bcachefs/str_hash.c | 25 +++++++++++++++---------- fs/bcachefs/str_hash.h | 9 ++++++--- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c index 62bb88250861..0d77b7e3632c 100644 --- a/fs/bcachefs/dirent.c +++ b/fs/bcachefs/dirent.c @@ -705,8 +705,9 @@ int bch2_readdir(struct bch_fs *c, subvol_inum inum, subvol_inum target; + bool need_second_pass = false; int ret2 = bch2_str_hash_check_key(trans, NULL, &bch2_dirent_hash_desc, - hash_info, &iter, k) ?: + hash_info, &iter, k, &need_second_pass) ?: bch2_dirent_read_target(trans, inum, dirent, &target); if (ret2 > 0) continue; diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index a7bf949df816..458f61c33958 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -2141,7 +2141,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter, struct bch_hash_info *hash_info, struct inode_walker *dir, struct inode_walker *target, - struct snapshots_seen *s) + struct snapshots_seen *s, + bool *need_second_pass) { struct bch_fs *c = trans->c; struct inode_walker_entry *i; @@ -2183,7 +2184,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter, *hash_info = bch2_hash_info_init(c, &i->inode); dir->first_this_inode = false; - ret = bch2_str_hash_check_key(trans, s, &bch2_dirent_hash_desc, hash_info, iter, k); + ret = bch2_str_hash_check_key(trans, s, &bch2_dirent_hash_desc, hash_info, + iter, k, need_second_pass); if (ret < 0) goto err; if (ret) { @@ -2228,7 +2230,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter, STR_HASH_must_create) ?: bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc); - /* might need another check_dirents pass */ + if (dir_offset < k.k->p.offset) + *need_second_pass = true; goto out; } @@ -2296,7 +2299,6 @@ out: err: fsck_err: printbuf_exit(&buf); - bch_err_fn(c, ret); return ret; } @@ -2310,17 +2312,30 @@ int bch2_check_dirents(struct bch_fs *c) struct inode_walker target = inode_walker_init(); struct snapshots_seen s; struct bch_hash_info hash_info; + bool need_second_pass = false, did_second_pass = false; snapshots_seen_init(&s); - +again: int ret = bch2_trans_run(c, for_each_btree_key_commit(trans, iter, BTREE_ID_dirents, POS(BCACHEFS_ROOT_INO, 0), BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, NULL, NULL, BCH_TRANS_COMMIT_no_enospc, - check_dirent(trans, &iter, k, &hash_info, &dir, &target, &s)) ?: + check_dirent(trans, &iter, k, &hash_info, &dir, &target, &s, + &need_second_pass)) ?: check_subdir_count_notnested(trans, &dir)); + if (!ret && need_second_pass && !did_second_pass) { + bch_info(c, "check_dirents requires second pass"); + swap(did_second_pass, need_second_pass); + goto again; + } + + if (!ret && need_second_pass) { + bch_err(c, "dirents not repairing"); + ret = -EINVAL; + } + snapshots_seen_exit(&s); inode_walker_exit(&dir); inode_walker_exit(&target); @@ -2334,16 +2349,14 @@ static int check_xattr(struct btree_trans *trans, struct btree_iter *iter, struct inode_walker *inode) { struct bch_fs *c = trans->c; - struct inode_walker_entry *i; - int ret; - ret = bch2_check_key_has_snapshot(trans, iter, k); + int ret = bch2_check_key_has_snapshot(trans, iter, k); if (ret < 0) return ret; if (ret) return 0; - i = walk_inode(trans, inode, k); + struct inode_walker_entry *i = walk_inode(trans, inode, k); ret = PTR_ERR_OR_ZERO(i); if (ret) return ret; @@ -2359,9 +2372,9 @@ static int check_xattr(struct btree_trans *trans, struct btree_iter *iter, *hash_info = bch2_hash_info_init(c, &i->inode); inode->first_this_inode = false; - ret = bch2_str_hash_check_key(trans, NULL, &bch2_xattr_hash_desc, hash_info, iter, k); - bch_err_fn(c, ret); - return ret; + bool need_second_pass = false; + return bch2_str_hash_check_key(trans, NULL, &bch2_xattr_hash_desc, hash_info, + iter, k, &need_second_pass); } /* diff --git a/fs/bcachefs/str_hash.c b/fs/bcachefs/str_hash.c index 36d7ca7b0b1a..e6ecc8a549ba 100644 --- a/fs/bcachefs/str_hash.c +++ b/fs/bcachefs/str_hash.c @@ -35,7 +35,8 @@ static noinline int fsck_rename_dirent(struct btree_trans *trans, struct snapshots_seen *s, const struct bch_hash_desc desc, struct bch_hash_info *hash_info, - struct bkey_s_c_dirent old) + struct bkey_s_c_dirent old, + bool *updated_before_k_pos) { struct qstr old_name = bch2_dirent_get_name(old); struct bkey_i_dirent *new = bch2_trans_kmalloc(trans, bkey_bytes(old.k) + 32); @@ -62,16 +63,15 @@ static noinline int fsck_rename_dirent(struct btree_trans *trans, old.k->p.snapshot, &new->k_i, BTREE_UPDATE_internal_snapshot_node); if (ret && !bch2_err_matches(ret, EEXIST)) - goto err; - if (!ret) break; + if (!ret) { + if (bpos_lt(new->k.p, old.k->p)) + *updated_before_k_pos = true; + break; + } } - if (ret) - goto err; - - ret = bch2_fsck_update_backpointers(trans, s, desc, hash_info, &new->k_i); -err: + ret = ret ?: bch2_fsck_update_backpointers(trans, s, desc, hash_info, &new->k_i); bch_err_fn(trans->c, ret); return ret; } @@ -230,7 +230,8 @@ int __bch2_str_hash_check_key(struct btree_trans *trans, struct snapshots_seen *s, const struct bch_hash_desc *desc, struct bch_hash_info *hash_info, - struct btree_iter *k_iter, struct bkey_s_c hash_k) + struct btree_iter *k_iter, struct bkey_s_c hash_k, + bool *updated_before_k_pos) { struct bch_fs *c = trans->c; struct btree_iter iter = {}; @@ -310,6 +311,9 @@ bad_hash: if (k.k) goto duplicate_entries; + if (bpos_lt(new->k.p, k.k->p)) + *updated_before_k_pos = true; + ret = bch2_insert_snapshot_whiteouts(trans, desc->btree_id, k_iter->pos, new->k.p) ?: bch2_hash_delete_at(trans, *desc, hash_info, k_iter, @@ -345,7 +349,8 @@ duplicate_entries: ret = bch2_hash_delete_at(trans, *desc, hash_info, &iter, 0); break; case 2: - ret = fsck_rename_dirent(trans, s, *desc, hash_info, bkey_s_c_to_dirent(hash_k)) ?: + ret = fsck_rename_dirent(trans, s, *desc, hash_info, bkey_s_c_to_dirent(hash_k), + updated_before_k_pos) ?: bch2_hash_delete_at(trans, *desc, hash_info, k_iter, 0); goto out; } diff --git a/fs/bcachefs/str_hash.h b/fs/bcachefs/str_hash.h index ebcc006825cd..2d31bc2358d0 100644 --- a/fs/bcachefs/str_hash.h +++ b/fs/bcachefs/str_hash.h @@ -402,13 +402,15 @@ int __bch2_str_hash_check_key(struct btree_trans *, struct snapshots_seen *, const struct bch_hash_desc *, struct bch_hash_info *, - struct btree_iter *, struct bkey_s_c); + struct btree_iter *, struct bkey_s_c, + bool *); static inline int bch2_str_hash_check_key(struct btree_trans *trans, struct snapshots_seen *s, const struct bch_hash_desc *desc, struct bch_hash_info *hash_info, - struct btree_iter *k_iter, struct bkey_s_c hash_k) + struct btree_iter *k_iter, struct bkey_s_c hash_k, + bool *updated_before_k_pos) { if (hash_k.k->type != desc->key_type) return 0; @@ -416,7 +418,8 @@ static inline int bch2_str_hash_check_key(struct btree_trans *trans, if (likely(desc->hash_bkey(hash_info, hash_k) == hash_k.k->p.offset)) return 0; - return __bch2_str_hash_check_key(trans, s, desc, hash_info, k_iter, hash_k); + return __bch2_str_hash_check_key(trans, s, desc, hash_info, k_iter, hash_k, + updated_before_k_pos); } #endif /* _BCACHEFS_STR_HASH_H */ -- 2.20.1