bcachefs: fsck: Fix check_path_loop() + snapshots
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 20:16:38 +0000 (16:16 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 23:05:02 +0000 (19:05 -0400)
A path exists in a particular snapshot: we should do the pathwalk in the
snapshot ID of the inode we started from, _not_ change snapshot ID as we
walk inodes and dirents.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fsck.c

index 96b9ea58705fa1113ee4e0665b366d6802da3e47..73cff24598edfdc56c73e5db1acb963a9bd2416f 100644 (file)
@@ -2689,19 +2689,13 @@ int bch2_check_subvolume_structure(struct bch_fs *c)
        return ret;
 }
 
-struct pathbuf_entry {
-       u64     inum;
-       u32     snapshot;
-};
-
-typedef DARRAY(struct pathbuf_entry) pathbuf;
-
-static int bch2_bi_depth_renumber_one(struct btree_trans *trans, struct pathbuf_entry *p,
+static int bch2_bi_depth_renumber_one(struct btree_trans *trans,
+                                     u64 inum, u32 snapshot,
                                      u32 new_depth)
 {
        struct btree_iter iter;
        struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
-                                              SPOS(0, p->inum, p->snapshot), 0);
+                                              SPOS(0, inum, snapshot), 0);
 
        struct bch_inode_unpacked inode;
        int ret = bkey_err(k) ?:
@@ -2720,14 +2714,15 @@ err:
        return ret;
 }
 
-static int bch2_bi_depth_renumber(struct btree_trans *trans, pathbuf *path, u32 new_bi_depth)
+static int bch2_bi_depth_renumber(struct btree_trans *trans, darray_u64 *path,
+                                 u32 snapshot, u32 new_bi_depth)
 {
        u32 restart_count = trans->restart_count;
        int ret = 0;
 
        darray_for_each_reverse(*path, i) {
                ret = nested_lockrestart_do(trans,
-                               bch2_bi_depth_renumber_one(trans, i, new_bi_depth));
+                               bch2_bi_depth_renumber_one(trans, *i, snapshot, new_bi_depth));
                bch_err_fn(trans->c, ret);
                if (ret)
                        break;
@@ -2738,26 +2733,19 @@ static int bch2_bi_depth_renumber(struct btree_trans *trans, pathbuf *path, u32
        return ret ?: trans_was_restarted(trans, restart_count);
 }
 
-static bool path_is_dup(pathbuf *p, u64 inum, u32 snapshot)
-{
-       darray_for_each(*p, i)
-               if (i->inum     == inum &&
-                   i->snapshot == snapshot)
-                       return true;
-       return false;
-}
-
 static int check_path_loop(struct btree_trans *trans, struct bkey_s_c inode_k)
 {
        struct bch_fs *c = trans->c;
        struct btree_iter inode_iter = {};
-       pathbuf path = {};
+       darray_u64 path = {};
        struct printbuf buf = PRINTBUF;
        u32 snapshot = inode_k.k->p.snapshot;
        bool redo_bi_depth = false;
        u32 min_bi_depth = U32_MAX;
        int ret = 0;
 
+       struct bpos start = inode_k.k->p;
+
        struct bch_inode_unpacked inode;
        ret = bch2_inode_unpack(inode_k, &inode);
        if (ret)
@@ -2766,9 +2754,9 @@ static int check_path_loop(struct btree_trans *trans, struct bkey_s_c inode_k)
        while (!inode.bi_subvol) {
                struct btree_iter dirent_iter;
                struct bkey_s_c_dirent d;
-               u32 parent_snapshot = snapshot;
 
-               d = inode_get_dirent(trans, &dirent_iter, &inode, &parent_snapshot);
+               d = dirent_get_by_pos(trans, &dirent_iter,
+                                     SPOS(inode.bi_dir, inode.bi_dir_offset, snapshot));
                ret = bkey_err(d.s_c);
                if (ret && !bch2_err_matches(ret, ENOENT))
                        goto out;
@@ -2786,15 +2774,10 @@ static int check_path_loop(struct btree_trans *trans, struct bkey_s_c inode_k)
 
                bch2_trans_iter_exit(trans, &dirent_iter);
 
-               ret = darray_push(&path, ((struct pathbuf_entry) {
-                       .inum           = inode.bi_inum,
-                       .snapshot       = snapshot,
-               }));
+               ret = darray_push(&path, inode.bi_inum);
                if (ret)
                        return ret;
 
-               snapshot = parent_snapshot;
-
                bch2_trans_iter_exit(trans, &inode_iter);
                inode_k = bch2_bkey_get_iter(trans, &inode_iter, BTREE_ID_inodes,
                                             SPOS(0, inode.bi_dir, snapshot), 0);
@@ -2816,15 +2799,22 @@ static int check_path_loop(struct btree_trans *trans, struct bkey_s_c inode_k)
                        break;
 
                inode = parent_inode;
-               snapshot = inode_k.k->p.snapshot;
                redo_bi_depth = true;
 
-               if (path_is_dup(&path, inode.bi_inum, snapshot)) {
+               if (darray_find(path, inode.bi_inum)) {
                        printbuf_reset(&buf);
-                       prt_printf(&buf, "directory structure loop:\n");
-                       darray_for_each_reverse(path, i)
-                               prt_printf(&buf, "%llu:%u ", i->inum, i->snapshot);
-                       prt_printf(&buf, "%llu:%u", inode.bi_inum, snapshot);
+                       prt_printf(&buf, "directory structure loop in snapshot %u: ",
+                                  snapshot);
+
+                       ret = bch2_inum_snapshot_to_path(trans, start.offset, start.snapshot, NULL, &buf);
+                       if (ret)
+                               goto out;
+
+                       if (c->opts.verbose) {
+                               prt_newline(&buf);
+                               darray_for_each(path, i)
+                                       prt_printf(&buf, "%llu ", *i);
+                       }
 
                        if (fsck_err(trans, dir_loop, "%s", buf.buf)) {
                                ret = remove_backpointer(trans, &inode);
@@ -2844,7 +2834,7 @@ static int check_path_loop(struct btree_trans *trans, struct bkey_s_c inode_k)
                min_bi_depth = 0;
 
        if (redo_bi_depth)
-               ret = bch2_bi_depth_renumber(trans, &path, min_bi_depth);
+               ret = bch2_bi_depth_renumber(trans, &path, snapshot, min_bi_depth);
 out:
 fsck_err:
        bch2_trans_iter_exit(trans, &inode_iter);