bcachefs: Fix __bch2_inum_to_path() when crossing subvol boundaries
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 18:00:40 +0000 (14:00 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 23:04:48 +0000 (19:04 -0400)
The bch2_subvolume_get_snapshot() call needs to happen before the dirent
lookup - the dirent is in the parent subvolume.

Also, check for loops.

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

index 779c22eb39794cc7e4f1215d11a51aa3ee6ba186..7d1068aa998f91585035ba2a3ef5874eb9b8974a 100644 (file)
@@ -625,14 +625,26 @@ static int __bch2_inum_to_path(struct btree_trans *trans,
 {
        unsigned orig_pos = path->pos;
        int ret = 0;
+       DARRAY(subvol_inum) inums = {};
+
+       if (!snapshot) {
+               ret = bch2_subvolume_get_snapshot(trans, subvol, &snapshot);
+               if (ret)
+                       goto disconnected;
+       }
 
        while (true) {
-               if (!snapshot) {
-                       ret = bch2_subvolume_get_snapshot(trans, subvol, &snapshot);
-                       if (ret)
-                               goto disconnected;
+               subvol_inum n = (subvol_inum) { subvol ?: snapshot, inum };
+
+               if (darray_find_p(inums, i, i->subvol == n.subvol && i->inum == n.inum)) {
+                       prt_str_reversed(path, "(loop)");
+                       break;
                }
 
+               ret = darray_push(&inums, n);
+               if (ret)
+                       goto err;
+
                struct bch_inode_unpacked inode;
                ret = bch2_inode_find_by_inum_snapshot(trans, inum, snapshot, &inode, 0);
                if (ret)
@@ -650,7 +662,9 @@ static int __bch2_inum_to_path(struct btree_trans *trans,
                inum = inode.bi_dir;
                if (inode.bi_parent_subvol) {
                        subvol = inode.bi_parent_subvol;
-                       snapshot = 0;
+                       ret = bch2_subvolume_get_snapshot(trans, inode.bi_parent_subvol, &snapshot);
+                       if (ret)
+                               goto disconnected;
                }
 
                struct btree_iter d_iter;
@@ -662,6 +676,7 @@ static int __bch2_inum_to_path(struct btree_trans *trans,
                        goto disconnected;
 
                struct qstr dirent_name = bch2_dirent_get_name(d);
+
                prt_bytes_reversed(path, dirent_name.name, dirent_name.len);
 
                prt_char(path, '/');
@@ -677,8 +692,10 @@ out:
                goto err;
 
        reverse_bytes(path->buf + orig_pos, path->pos - orig_pos);
+       darray_exit(&inums);
        return 0;
 err:
+       darray_exit(&inums);
        return ret;
 disconnected:
        if (bch2_err_matches(ret, BCH_ERR_transaction_restart))