bcachefs: factor out str_hash.c
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 5 Dec 2024 04:36:33 +0000 (23:36 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 21 Dec 2024 06:36:22 +0000 (01:36 -0500)
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/Makefile
fs/bcachefs/fsck.c
fs/bcachefs/fsck.h
fs/bcachefs/str_hash.c [new file with mode: 0644]
fs/bcachefs/str_hash.h

index 56d20e219f5910f28fbefebfee676001639ef868..d2689388d5e88e0aae78fc7cc23c193a9e0727bd 100644 (file)
@@ -82,6 +82,7 @@ bcachefs-y            :=      \
        siphash.o               \
        six.o                   \
        snapshot.o              \
+       str_hash.o              \
        subvolume.o             \
        super.o                 \
        super-io.o              \
index 1e00b2694db725825fccc260e047e8beb5687657..22a33b9ba30d2071a489560c334ba66462cf4163 100644 (file)
@@ -941,69 +941,16 @@ static int get_visible_inodes(struct btree_trans *trans,
        return ret;
 }
 
-static int dirent_has_target(struct btree_trans *trans, struct bkey_s_c_dirent d)
-{
-       if (d.v->d_type == DT_SUBVOL) {
-               u32 snap;
-               u64 inum;
-               int ret = subvol_lookup(trans, le32_to_cpu(d.v->d_child_subvol), &snap, &inum);
-               if (ret && !bch2_err_matches(ret, ENOENT))
-                       return ret;
-               return !ret;
-       } else {
-               struct btree_iter iter;
-               struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
-                               SPOS(0, le64_to_cpu(d.v->d_inum), d.k->p.snapshot), 0);
-               int ret = bkey_err(k);
-               if (ret)
-                       return ret;
-
-               ret = bkey_is_inode(k.k);
-               bch2_trans_iter_exit(trans, &iter);
-               return ret;
-       }
-}
-
 /*
  * Prefer to delete the first one, since that will be the one at the wrong
  * offset:
  * return value: 0 -> delete k1, 1 -> delete k2
  */
-static int hash_pick_winner(struct btree_trans *trans,
-                           const struct bch_hash_desc desc,
-                           struct bch_hash_info *hash_info,
-                           struct bkey_s_c k1,
-                           struct bkey_s_c k2)
-{
-       if (bkey_val_bytes(k1.k) == bkey_val_bytes(k2.k) &&
-           !memcmp(k1.v, k2.v, bkey_val_bytes(k1.k)))
-               return 0;
-
-       switch (desc.btree_id) {
-       case BTREE_ID_dirents: {
-               int ret = dirent_has_target(trans, bkey_s_c_to_dirent(k1));
-               if (ret < 0)
-                       return ret;
-               if (!ret)
-                       return 0;
-
-               ret = dirent_has_target(trans, bkey_s_c_to_dirent(k2));
-               if (ret < 0)
-                       return ret;
-               if (!ret)
-                       return 1;
-               return 2;
-       }
-       default:
-               return 0;
-       }
-}
-
-static int fsck_update_backpointers(struct btree_trans *trans,
-                                   struct snapshots_seen *s,
-                                   const struct bch_hash_desc desc,
-                                   struct bch_hash_info *hash_info,
-                                   struct bkey_i *new)
+int bch2_fsck_update_backpointers(struct btree_trans *trans,
+                                 struct snapshots_seen *s,
+                                 const struct bch_hash_desc desc,
+                                 struct bch_hash_info *hash_info,
+                                 struct bkey_i *new)
 {
        if (new->k.type != KEY_TYPE_dirent)
                return 0;
@@ -1031,153 +978,6 @@ err:
        return ret;
 }
 
-static 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 qstr old_name = bch2_dirent_get_name(old);
-       struct bkey_i_dirent *new = bch2_trans_kmalloc(trans, bkey_bytes(old.k) + 32);
-       int ret = PTR_ERR_OR_ZERO(new);
-       if (ret)
-               return ret;
-
-       bkey_dirent_init(&new->k_i);
-       dirent_copy_target(new, old);
-       new->k.p = old.k->p;
-
-       for (unsigned i = 0; i < 1000; i++) {
-               unsigned len = sprintf(new->v.d_name, "%.*s.fsck_renamed-%u",
-                                      old_name.len, old_name.name, i);
-               unsigned u64s = BKEY_U64s + dirent_val_u64s(len);
-
-               if (u64s > U8_MAX)
-                       return -EINVAL;
-
-               new->k.u64s = u64s;
-
-               ret = bch2_hash_set_in_snapshot(trans, bch2_dirent_hash_desc, hash_info,
-                                               (subvol_inum) { 0, old.k->p.inode },
-                                               old.k->p.snapshot, &new->k_i,
-                                               BTREE_UPDATE_internal_snapshot_node);
-               if (!bch2_err_matches(ret, EEXIST))
-                       break;
-       }
-
-       if (ret)
-               return ret;
-
-       return fsck_update_backpointers(trans, s, desc, hash_info, &new->k_i);
-}
-
-static int 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 bch_fs *c = trans->c;
-       struct btree_iter iter = { NULL };
-       struct printbuf buf = PRINTBUF;
-       struct bkey_s_c k;
-       u64 hash;
-       int ret = 0;
-
-       if (hash_k.k->type != desc.key_type)
-               return 0;
-
-       hash = desc.hash_bkey(hash_info, hash_k);
-
-       if (likely(hash == hash_k.k->p.offset))
-               return 0;
-
-       if (hash_k.k->p.offset < hash)
-               goto bad_hash;
-
-       for_each_btree_key_norestart(trans, iter, desc.btree_id,
-                                    SPOS(hash_k.k->p.inode, hash, hash_k.k->p.snapshot),
-                                    BTREE_ITER_slots, k, ret) {
-               if (bkey_eq(k.k->p, hash_k.k->p))
-                       break;
-
-               if (k.k->type == desc.key_type &&
-                   !desc.cmp_bkey(k, hash_k))
-                       goto duplicate_entries;
-
-               if (bkey_deleted(k.k)) {
-                       bch2_trans_iter_exit(trans, &iter);
-                       goto bad_hash;
-               }
-       }
-out:
-       bch2_trans_iter_exit(trans, &iter);
-       printbuf_exit(&buf);
-       return ret;
-bad_hash:
-       if (fsck_err(trans, hash_table_key_wrong_offset,
-                    "hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n  %s",
-                    bch2_btree_id_str(desc.btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
-                    (printbuf_reset(&buf),
-                     bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
-               struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, hash_k);
-               if (IS_ERR(new))
-                       return PTR_ERR(new);
-
-               k = bch2_hash_set_or_get_in_snapshot(trans, &iter, desc, hash_info,
-                                      (subvol_inum) { 0, hash_k.k->p.inode },
-                                      hash_k.k->p.snapshot, new,
-                                      STR_HASH_must_create|
-                                      BTREE_ITER_with_updates|
-                                      BTREE_UPDATE_internal_snapshot_node);
-               ret = bkey_err(k);
-               if (ret)
-                       goto out;
-               if (k.k)
-                       goto duplicate_entries;
-
-               ret =   bch2_hash_delete_at(trans, desc, hash_info, k_iter,
-                                           BTREE_UPDATE_internal_snapshot_node) ?:
-                       fsck_update_backpointers(trans, s, desc, hash_info, new) ?:
-                       bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
-                       -BCH_ERR_transaction_restart_nested;
-               goto out;
-       }
-fsck_err:
-       goto out;
-duplicate_entries:
-       ret = hash_pick_winner(trans, desc, hash_info, hash_k, k);
-       if (ret < 0)
-               goto out;
-
-       if (!fsck_err(trans, hash_table_key_duplicate,
-                     "duplicate hash table keys%s:\n%s",
-                     ret != 2 ? "" : ", both point to valid inodes",
-                     (printbuf_reset(&buf),
-                      bch2_bkey_val_to_text(&buf, c, hash_k),
-                      prt_newline(&buf),
-                      bch2_bkey_val_to_text(&buf, c, k),
-                      buf.buf)))
-               goto out;
-
-       switch (ret) {
-       case 0:
-               ret = bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0);
-               break;
-       case 1:
-               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)) ?:
-                       bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0);
-               goto out;
-       }
-
-       ret = bch2_trans_commit(trans, NULL, NULL, 0) ?:
-               -BCH_ERR_transaction_restart_nested;
-       goto out;
-}
-
 static struct bkey_s_c_dirent inode_get_dirent(struct btree_trans *trans,
                                               struct btree_iter *iter,
                                               struct bch_inode_unpacked *inode,
@@ -2496,7 +2296,7 @@ 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 = 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);
        if (ret < 0)
                goto err;
        if (ret) {
@@ -2610,7 +2410,7 @@ 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 = hash_check_key(trans, NULL, bch2_xattr_hash_desc, hash_info, iter, k);
+       ret = bch2_str_hash_check_key(trans, NULL, bch2_xattr_hash_desc, hash_info, iter, k);
        bch_err_fn(c, ret);
        return ret;
 }
index 4481b40a881d8cf9ad1314b975c7f23d1372e84d..574948278cd4dae9f8a870a6f5dd5ff9101a9e1c 100644 (file)
@@ -2,6 +2,14 @@
 #ifndef _BCACHEFS_FSCK_H
 #define _BCACHEFS_FSCK_H
 
+#include "str_hash.h"
+
+int bch2_fsck_update_backpointers(struct btree_trans *,
+                                 struct snapshots_seen *,
+                                 const struct bch_hash_desc,
+                                 struct bch_hash_info *,
+                                 struct bkey_i *);
+
 int bch2_check_inodes(struct bch_fs *);
 int bch2_check_extents(struct bch_fs *);
 int bch2_check_indirect_extents(struct bch_fs *);
diff --git a/fs/bcachefs/str_hash.c b/fs/bcachefs/str_hash.c
new file mode 100644 (file)
index 0000000..c3276a7
--- /dev/null
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "bcachefs.h"
+#include "btree_cache.h"
+#include "btree_update.h"
+#include "dirent.h"
+#include "fsck.h"
+#include "str_hash.h"
+#include "subvolume.h"
+
+static int bch2_dirent_has_target(struct btree_trans *trans, struct bkey_s_c_dirent d)
+{
+       if (d.v->d_type == DT_SUBVOL) {
+               struct bch_subvolume subvol;
+               int ret = bch2_subvolume_get(trans, le32_to_cpu(d.v->d_child_subvol),
+                                            false, &subvol);
+               if (ret && !bch2_err_matches(ret, ENOENT))
+                       return ret;
+               return !ret;
+       } else {
+               struct btree_iter iter;
+               struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
+                               SPOS(0, le64_to_cpu(d.v->d_inum), d.k->p.snapshot), 0);
+               int ret = bkey_err(k);
+               if (ret)
+                       return ret;
+
+               ret = bkey_is_inode(k.k);
+               bch2_trans_iter_exit(trans, &iter);
+               return ret;
+       }
+}
+
+static 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 qstr old_name = bch2_dirent_get_name(old);
+       struct bkey_i_dirent *new = bch2_trans_kmalloc(trans, bkey_bytes(old.k) + 32);
+       int ret = PTR_ERR_OR_ZERO(new);
+       if (ret)
+               return ret;
+
+       bkey_dirent_init(&new->k_i);
+       dirent_copy_target(new, old);
+       new->k.p = old.k->p;
+
+       for (unsigned i = 0; i < 1000; i++) {
+               unsigned len = sprintf(new->v.d_name, "%.*s.fsck_renamed-%u",
+                                      old_name.len, old_name.name, i);
+               unsigned u64s = BKEY_U64s + dirent_val_u64s(len);
+
+               if (u64s > U8_MAX)
+                       return -EINVAL;
+
+               new->k.u64s = u64s;
+
+               ret = bch2_hash_set_in_snapshot(trans, bch2_dirent_hash_desc, hash_info,
+                                               (subvol_inum) { 0, old.k->p.inode },
+                                               old.k->p.snapshot, &new->k_i,
+                                               BTREE_UPDATE_internal_snapshot_node);
+               if (!bch2_err_matches(ret, EEXIST))
+                       break;
+       }
+
+       if (ret)
+               return ret;
+
+       return bch2_fsck_update_backpointers(trans, s, desc, hash_info, &new->k_i);
+}
+
+static int hash_pick_winner(struct btree_trans *trans,
+                           const struct bch_hash_desc desc,
+                           struct bch_hash_info *hash_info,
+                           struct bkey_s_c k1,
+                           struct bkey_s_c k2)
+{
+       if (bkey_val_bytes(k1.k) == bkey_val_bytes(k2.k) &&
+           !memcmp(k1.v, k2.v, bkey_val_bytes(k1.k)))
+               return 0;
+
+       switch (desc.btree_id) {
+       case BTREE_ID_dirents: {
+               int ret = bch2_dirent_has_target(trans, bkey_s_c_to_dirent(k1));
+               if (ret < 0)
+                       return ret;
+               if (!ret)
+                       return 0;
+
+               ret = bch2_dirent_has_target(trans, bkey_s_c_to_dirent(k2));
+               if (ret < 0)
+                       return ret;
+               if (!ret)
+                       return 1;
+               return 2;
+       }
+       default:
+               return 0;
+       }
+}
+
+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 bch_fs *c = trans->c;
+       struct btree_iter iter = { NULL };
+       struct printbuf buf = PRINTBUF;
+       struct bkey_s_c k;
+       u64 hash;
+       int ret = 0;
+
+       if (hash_k.k->type != desc.key_type)
+               return 0;
+
+       hash = desc.hash_bkey(hash_info, hash_k);
+
+       if (likely(hash == hash_k.k->p.offset))
+               return 0;
+
+       if (hash_k.k->p.offset < hash)
+               goto bad_hash;
+
+       for_each_btree_key_norestart(trans, iter, desc.btree_id,
+                                    SPOS(hash_k.k->p.inode, hash, hash_k.k->p.snapshot),
+                                    BTREE_ITER_slots, k, ret) {
+               if (bkey_eq(k.k->p, hash_k.k->p))
+                       break;
+
+               if (k.k->type == desc.key_type &&
+                   !desc.cmp_bkey(k, hash_k))
+                       goto duplicate_entries;
+
+               if (bkey_deleted(k.k)) {
+                       bch2_trans_iter_exit(trans, &iter);
+                       goto bad_hash;
+               }
+       }
+out:
+       bch2_trans_iter_exit(trans, &iter);
+       printbuf_exit(&buf);
+       return ret;
+bad_hash:
+       if (fsck_err(trans, hash_table_key_wrong_offset,
+                    "hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n  %s",
+                    bch2_btree_id_str(desc.btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
+                    (printbuf_reset(&buf),
+                     bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
+               struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, hash_k);
+               if (IS_ERR(new))
+                       return PTR_ERR(new);
+
+               k = bch2_hash_set_or_get_in_snapshot(trans, &iter, desc, hash_info,
+                                      (subvol_inum) { 0, hash_k.k->p.inode },
+                                      hash_k.k->p.snapshot, new,
+                                      STR_HASH_must_create|
+                                      BTREE_ITER_with_updates|
+                                      BTREE_UPDATE_internal_snapshot_node);
+               ret = bkey_err(k);
+               if (ret)
+                       goto out;
+               if (k.k)
+                       goto duplicate_entries;
+
+               ret =   bch2_hash_delete_at(trans, desc, hash_info, k_iter,
+                                           BTREE_UPDATE_internal_snapshot_node) ?:
+                       bch2_fsck_update_backpointers(trans, s, desc, hash_info, new) ?:
+                       bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
+                       -BCH_ERR_transaction_restart_nested;
+               goto out;
+       }
+fsck_err:
+       goto out;
+duplicate_entries:
+       ret = hash_pick_winner(trans, desc, hash_info, hash_k, k);
+       if (ret < 0)
+               goto out;
+
+       if (!fsck_err(trans, hash_table_key_duplicate,
+                     "duplicate hash table keys%s:\n%s",
+                     ret != 2 ? "" : ", both point to valid inodes",
+                     (printbuf_reset(&buf),
+                      bch2_bkey_val_to_text(&buf, c, hash_k),
+                      prt_newline(&buf),
+                      bch2_bkey_val_to_text(&buf, c, k),
+                      buf.buf)))
+               goto out;
+
+       switch (ret) {
+       case 0:
+               ret = bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0);
+               break;
+       case 1:
+               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)) ?:
+                       bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0);
+               goto out;
+       }
+
+       ret = bch2_trans_commit(trans, NULL, NULL, 0) ?:
+               -BCH_ERR_transaction_restart_nested;
+       goto out;
+}
index 00c785055d22b971fd0c334fce13c0a44dba325b..0c20f3af03f85ded1c19349e3373b620e79fdba0 100644 (file)
@@ -393,4 +393,11 @@ int bch2_hash_delete(struct btree_trans *trans,
        return ret;
 }
 
+struct snapshots_seen;
+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);
+
 #endif /* _BCACHEFS_STR_HASH_H */