]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bcachefs: Improve bch2_repair_inode_hash_info()
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 15 May 2025 12:41:26 +0000 (08:41 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:15:00 +0000 (20:15 -0400)
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>
fs/bcachefs/fsck.c
fs/bcachefs/str_hash.c
fs/bcachefs/str_hash.h

index e7cac5a6915463725046d3ee11bd5b51d6f76cfd..2b7bc67dcdf8ebcb671436922b780ff4e925cc9e 100644 (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) {
index 2d6379473ad4b37496f835124506321c1ad9d070..0cbf5508a32c8b75729e29462b3aaef1f9ba806b 100644 (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 @@ fsck_err:
 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;
 }
index ae3154fb6a9441f0d427fb103e51708820d9c19f..6762b3627e1b63ca53794cd94f1da2c11ffdd7cf 100644 (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 *,