bcachefs: Improve bch2_repair_inode_hash_info()

Improve this so it can be used by fsck.c check_inode(); it provides a
much better error message than the check_inode() version.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet
2025-05-15 08:41:26 -04:00
parent 123d2d09ff
commit fdd0807f81
3 changed files with 86 additions and 41 deletions

View File

@@ -1150,13 +1150,12 @@ static int check_inode(struct btree_trans *trans,
goto err;
}
if (fsck_err_on(u.bi_hash_seed != snapshot_root->bi_hash_seed ||
INODE_STR_HASH(&u) != INODE_STR_HASH(snapshot_root),
trans, inode_snapshot_mismatch,
"inode hash info in different snapshots don't match")) {
u.bi_hash_seed = snapshot_root->bi_hash_seed;
SET_INODE_STR_HASH(&u, INODE_STR_HASH(snapshot_root));
do_update = true;
if (u.bi_hash_seed != snapshot_root->bi_hash_seed ||
INODE_STR_HASH(&u) != INODE_STR_HASH(snapshot_root)) {
ret = bch2_repair_inode_hash_info(trans, snapshot_root);
BUG_ON(ret == -BCH_ERR_fsck_repair_unimplemented);
if (ret)
goto err;
}
if (u.bi_dir || u.bi_dir_offset) {

View File

@@ -101,17 +101,25 @@ static noinline int hash_pick_winner(struct btree_trans *trans,
}
}
static int repair_inode_hash_info(struct btree_trans *trans,
struct bch_inode_unpacked *snapshot_root)
/*
* str_hash lookups across snapshots break in wild ways if hash_info in
* different snapshot versions doesn't match - so if we find one mismatch, check
* them all
*/
int bch2_repair_inode_hash_info(struct btree_trans *trans,
struct bch_inode_unpacked *snapshot_root)
{
struct bch_fs *c = trans->c;
struct btree_iter iter;
struct bkey_s_c k;
struct printbuf buf = PRINTBUF;
bool need_commit = false;
int ret = 0;
for_each_btree_key_reverse_norestart(trans, iter, BTREE_ID_inodes,
SPOS(0, snapshot_root->bi_inum, snapshot_root->bi_snapshot - 1),
BTREE_ITER_all_snapshots, k, ret) {
if (k.k->p.offset != snapshot_root->bi_inum)
for_each_btree_key_norestart(trans, iter, BTREE_ID_inodes,
POS(0, snapshot_root->bi_inum),
BTREE_ITER_all_snapshots, k, ret) {
if (bpos_ge(k.k->p, SPOS(0, snapshot_root->bi_inum, snapshot_root->bi_snapshot)))
break;
if (!bkey_is_inode(k.k))
continue;
@@ -121,19 +129,72 @@ static int repair_inode_hash_info(struct btree_trans *trans,
if (ret)
break;
if (fsck_err_on(inode.bi_hash_seed != snapshot_root->bi_hash_seed ||
INODE_STR_HASH(&inode) != INODE_STR_HASH(snapshot_root),
trans, inode_snapshot_mismatch,
"inode hash info in different snapshots don't match")) {
if (inode.bi_hash_seed == snapshot_root->bi_hash_seed &&
INODE_STR_HASH(&inode) == INODE_STR_HASH(snapshot_root)) {
#ifdef CONFIG_BCACHEFS_DEBUG
struct bch_hash_info hash1 = bch2_hash_info_init(c, snapshot_root);
struct bch_hash_info hash2 = bch2_hash_info_init(c, &inode);
BUG_ON(hash1.type != hash2.type ||
memcmp(&hash1.siphash_key,
&hash2.siphash_key,
sizeof(hash1.siphash_key)));
#endif
continue;
}
printbuf_reset(&buf);
prt_printf(&buf, "inode %llu hash info in snapshots %u %u don't match\n",
snapshot_root->bi_inum,
inode.bi_snapshot,
snapshot_root->bi_snapshot);
bch2_prt_str_hash_type(&buf, INODE_STR_HASH(&inode));
prt_printf(&buf, " %llx\n", inode.bi_hash_seed);
bch2_prt_str_hash_type(&buf, INODE_STR_HASH(snapshot_root));
prt_printf(&buf, " %llx", snapshot_root->bi_hash_seed);
if (fsck_err(trans, inode_snapshot_mismatch, "%s", buf.buf)) {
inode.bi_hash_seed = snapshot_root->bi_hash_seed;
SET_INODE_STR_HASH(&inode, INODE_STR_HASH(snapshot_root));
ret = __bch2_fsck_write_inode(trans, &inode) ?:
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
-BCH_ERR_transaction_restart_nested;
break;
ret = __bch2_fsck_write_inode(trans, &inode);
if (ret)
break;
need_commit = true;
}
}
if (ret)
goto err;
if (!need_commit) {
struct printbuf buf = PRINTBUF;
bch2_log_msg_start(c, &buf);
prt_printf(&buf, "inode %llu hash info mismatch with root, but mismatch not found\n",
snapshot_root->bi_inum);
prt_printf(&buf, "root snapshot %u ", snapshot_root->bi_snapshot);
bch2_prt_str_hash_type(&buf, INODE_STR_HASH(snapshot_root));
prt_printf(&buf, " %llx\n", snapshot_root->bi_hash_seed);
#if 0
prt_printf(&buf, "vs snapshot %u ", hash_info->inum_snapshot);
bch2_prt_str_hash_type(&buf, hash_info->type);
prt_printf(&buf, " %llx %llx", hash_info->siphash_key.k0, hash_info->siphash_key.k1);
#endif
bch2_print_str(c, KERN_ERR, buf.buf);
printbuf_exit(&buf);
ret = -BCH_ERR_fsck_repair_unimplemented;
goto err;
}
ret = bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
-BCH_ERR_transaction_restart_nested;
err:
fsck_err:
printbuf_exit(&buf);
bch2_trans_iter_exit(trans, &iter);
return ret;
}
@@ -145,34 +206,17 @@ static int repair_inode_hash_info(struct btree_trans *trans,
static noinline int check_inode_hash_info_matches_root(struct btree_trans *trans, u64 inum,
struct bch_hash_info *hash_info)
{
struct bch_fs *c = trans->c;
struct bch_inode_unpacked snapshot_root;
int ret = bch2_inode_find_snapshot_root(trans, inum, &snapshot_root);
if (ret)
return ret;
struct bch_hash_info hash_root = bch2_hash_info_init(c, &snapshot_root);
struct bch_hash_info hash_root = bch2_hash_info_init(trans->c, &snapshot_root);
if (hash_info->type != hash_root.type ||
memcmp(&hash_info->siphash_key,
&hash_root.siphash_key,
sizeof(hash_root.siphash_key))) {
ret = repair_inode_hash_info(trans, &snapshot_root);
if (!ret) {
struct printbuf buf = PRINTBUF;
prt_printf(&buf, "inode %llu hash info mismatch with root, but mismatch not found\n", inum);
prt_printf(&buf, "root snapshot %u ", hash_root.inum_snapshot);
bch2_prt_str_hash_type(&buf, hash_root.type);
prt_printf(&buf, " %llx %llx\n", hash_root.siphash_key.k0, hash_root.siphash_key.k1);
prt_printf(&buf, "vs snapshot %u ", hash_info->inum_snapshot);
bch2_prt_str_hash_type(&buf, hash_info->type);
prt_printf(&buf, " %llx %llx", hash_info->siphash_key.k0, hash_info->siphash_key.k1);
bch_err(c, "%s", buf.buf);
printbuf_exit(&buf);
ret = -BCH_ERR_fsck_repair_unimplemented;
}
}
sizeof(hash_root.siphash_key)))
ret = bch2_repair_inode_hash_info(trans, &snapshot_root);
return ret;
}

View File

@@ -394,6 +394,8 @@ int bch2_hash_delete(struct btree_trans *trans,
return ret;
}
int bch2_repair_inode_hash_info(struct btree_trans *, struct bch_inode_unpacked *);
struct snapshots_seen;
int __bch2_str_hash_check_key(struct btree_trans *,
struct snapshots_seen *,