]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
bcachefs: bch2_check_subvolume_structure()
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 16 Feb 2024 03:50:42 +0000 (22:50 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 14 Mar 2024 01:22:25 +0000 (21:22 -0400)
Now that we've got bch_subvolume.fs_path_parent, it's easy to write
subvolume

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

index 797a5711216c37052aef6e9630d86266029a459c..388f181eb0f2e626312a604d09b369527b462030 100644 (file)
@@ -342,6 +342,27 @@ static int remove_backpointer(struct btree_trans *trans,
        return ret;
 }
 
+static int reattach_subvol(struct btree_trans *trans, struct bkey_s_c_subvolume s)
+{
+       struct bch_fs *c = trans->c;
+
+       struct bch_inode_unpacked inode;
+       int ret = bch2_inode_find_by_inum_trans(trans,
+                               (subvol_inum) { s.k->p.offset, le64_to_cpu(s.v->inode) },
+                               &inode);
+       if (ret)
+               return ret;
+
+       ret = remove_backpointer(trans, &inode);
+       bch_err_msg(c, ret, "removing dirent");
+       if (ret)
+               return ret;
+
+       ret = reattach_inode(trans, &inode, le32_to_cpu(s.v->snapshot));
+       bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
+       return ret;
+}
+
 struct snapshots_seen_entry {
        u32                             id;
        u32                             equiv;
@@ -2108,6 +2129,107 @@ int bch2_check_root(struct bch_fs *c)
        return ret;
 }
 
+typedef DARRAY(u32) darray_u32;
+
+static bool darray_u32_has(darray_u32 *d, u32 v)
+{
+       darray_for_each(*d, i)
+               if (*i == v)
+                       return true;
+       return false;
+}
+
+/*
+ * We've checked that inode backpointers point to valid dirents; here, it's
+ * sufficient to check that the subvolume root has a dirent:
+ */
+static int subvol_has_dirent(struct btree_trans *trans, struct bkey_s_c_subvolume s)
+{
+       struct bch_inode_unpacked inode;
+       int ret = bch2_inode_find_by_inum_trans(trans,
+                               (subvol_inum) { s.k->p.offset, le64_to_cpu(s.v->inode) },
+                               &inode);
+       if (ret)
+               return ret;
+
+       return inode.bi_dir != 0;
+}
+
+static int check_subvol_path(struct btree_trans *trans, struct btree_iter *iter, struct bkey_s_c k)
+{
+       struct bch_fs *c = trans->c;
+       struct btree_iter parent_iter = {};
+       darray_u32 subvol_path = {};
+       struct printbuf buf = PRINTBUF;
+       int ret = 0;
+
+       if (k.k->type != KEY_TYPE_subvolume)
+               return 0;
+
+       while (k.k->p.offset != BCACHEFS_ROOT_SUBVOL) {
+               ret = darray_push(&subvol_path, k.k->p.offset);
+               if (ret)
+                       goto err;
+
+               struct bkey_s_c_subvolume s = bkey_s_c_to_subvolume(k);
+
+               ret = subvol_has_dirent(trans, s);
+               if (ret < 0)
+                       break;
+
+               if (fsck_err_on(!ret,
+                               c, subvol_unreachable,
+                               "unreachable subvolume %s",
+                               (bch2_bkey_val_to_text(&buf, c, s.s_c),
+                                buf.buf))) {
+                       ret = reattach_subvol(trans, s);
+                       break;
+               }
+
+               u32 parent = le32_to_cpu(s.v->fs_path_parent);
+
+               if (darray_u32_has(&subvol_path, parent)) {
+                       if (fsck_err(c, subvol_loop, "subvolume loop"))
+                               ret = reattach_subvol(trans, s);
+                       break;
+               }
+
+               bch2_trans_iter_exit(trans, &parent_iter);
+               bch2_trans_iter_init(trans, &parent_iter,
+                                    BTREE_ID_subvolumes, POS(0, parent), 0);
+               k = bch2_btree_iter_peek_slot(&parent_iter);
+               ret = bkey_err(k);
+               if (ret)
+                       goto err;
+
+               if (fsck_err_on(k.k->type != KEY_TYPE_subvolume,
+                               c, subvol_unreachable,
+                               "unreachable subvolume %s",
+                               (bch2_bkey_val_to_text(&buf, c, s.s_c),
+                                buf.buf))) {
+                       ret = reattach_subvol(trans, s);
+                       break;
+               }
+       }
+fsck_err:
+err:
+       printbuf_exit(&buf);
+       darray_exit(&subvol_path);
+       bch2_trans_iter_exit(trans, &parent_iter);
+       return ret;
+}
+
+int bch2_check_subvolume_structure(struct bch_fs *c)
+{
+       int ret = bch2_trans_run(c,
+               for_each_btree_key_commit(trans, iter,
+                               BTREE_ID_subvolumes, POS_MIN, BTREE_ITER_PREFETCH, k,
+                               NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
+                       check_subvol_path(trans, &iter, k)));
+       bch_err_fn(c, ret);
+       return ret;
+}
+
 struct pathbuf_entry {
        u64     inum;
        u32     snapshot;
@@ -2124,20 +2246,6 @@ static bool path_is_dup(pathbuf *p, u64 inum, u32 snapshot)
        return false;
 }
 
-static int path_down(struct bch_fs *c, pathbuf *p,
-                    u64 inum, u32 snapshot)
-{
-       int ret = darray_push(p, ((struct pathbuf_entry) {
-               .inum           = inum,
-               .snapshot       = snapshot,
-       }));
-
-       if (ret)
-               bch_err(c, "fsck: error allocating memory for pathbuf, size %zu",
-                       p->size);
-       return ret;
-}
-
 /*
  * Check that a given inode is reachable from the root:
  *
@@ -2188,11 +2296,12 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
                if (!S_ISDIR(inode.bi_mode))
                        break;
 
-               ret = path_down(c, p, inode.bi_inum, snapshot);
-               if (ret) {
-                       bch_err(c, "memory allocation failure");
+               ret = darray_push(p, ((struct pathbuf_entry) {
+                       .inum           = inode.bi_inum,
+                       .snapshot       = snapshot,
+               }));
+               if (ret)
                        return ret;
-               }
 
                snapshot = parent_snapshot;
 
@@ -2219,18 +2328,15 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
                                pr_err("%llu:%u", i->inum, i->snapshot);
                        pr_err("%llu:%u", inode.bi_inum, snapshot);
 
-                       if (!fsck_err(c, dir_loop, "directory structure loop"))
-                               return 0;
-
-                       ret = remove_backpointer(trans, &inode);
-                       if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
+                       if (fsck_err(c, dir_loop, "directory structure loop")) {
+                               ret = remove_backpointer(trans, &inode);
                                bch_err_msg(c, ret, "removing dirent");
-                       if (ret)
-                               break;
+                               if (ret)
+                                       break;
 
-                       ret = reattach_inode(trans, &inode, snapshot);
-                       if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
+                               ret = reattach_inode(trans, &inode, snapshot);
                                bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
+                       }
                        break;
                }
        }
index da991e8cf27eb493ed5aac5a3e3da606ae089968..a4ef9427178433bda33d500fe5f7551ff8fd1638 100644 (file)
@@ -8,6 +8,7 @@ int bch2_check_indirect_extents(struct bch_fs *);
 int bch2_check_dirents(struct bch_fs *);
 int bch2_check_xattrs(struct bch_fs *);
 int bch2_check_root(struct bch_fs *);
+int bch2_check_subvolume_structure(struct bch_fs *);
 int bch2_check_directory_structure(struct bch_fs *);
 int bch2_check_nlinks(struct bch_fs *);
 int bch2_fix_reflink_p(struct bch_fs *);
index f0fc1dbb7239296af6712bb0ecc6cc666cf9d36b..1361e34d4e64c2939fc0b7af8c9df9e5d9dfc7cf 100644 (file)
@@ -44,6 +44,7 @@
        x(check_dirents,                        27, PASS_FSCK)                  \
        x(check_xattrs,                         28, PASS_FSCK)                  \
        x(check_root,                           29, PASS_ONLINE|PASS_FSCK)      \
+       x(check_subvolume_structure,            36, PASS_ONLINE|PASS_FSCK)      \
        x(check_directory_structure,            30, PASS_ONLINE|PASS_FSCK)      \
        x(check_nlinks,                         31, PASS_FSCK)                  \
        x(delete_dead_inodes,                   32, PASS_FSCK|PASS_UNCLEAN)     \