bcachefs: fsck: Fix check_path_loop() + snapshots

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>
This commit is contained in:
Kent Overstreet
2025-06-16 16:16:38 -04:00
parent 583ba52a40
commit 495ba899d5
+26 -36
View 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);