]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/fsck.c
Update bcachefs sources to 50847e296b34 bcachefs: Check subvol <-> inode pointers...
[bcachefs-tools-debian] / libbcachefs / fsck.c
index 7e82c7bc0ca82289b36e4f32be5e0ce343874748..e4a8a14c46bc922983e91edcdc9ece6fe717d3eb 100644 (file)
@@ -5,7 +5,6 @@
 #include "btree_cache.h"
 #include "btree_update.h"
 #include "buckets.h"
-#include "darray.h"
 #include "dirent.h"
 #include "error.h"
 #include "fs-common.h"
@@ -18,6 +17,7 @@
 #include "xattr.h"
 
 #include <linux/bsearch.h>
+#include <linux/darray.h>
 #include <linux/dcache.h> /* struct qstr */
 
 /*
@@ -100,8 +100,8 @@ err:
 }
 
 static int lookup_inode(struct btree_trans *trans, u64 inode_nr,
-                         struct bch_inode_unpacked *inode,
-                         u32 *snapshot)
+                       struct bch_inode_unpacked *inode,
+                       u32 *snapshot)
 {
        struct btree_iter iter;
        struct bkey_s_c k;
@@ -123,17 +123,15 @@ err:
        return ret;
 }
 
-static int __lookup_dirent(struct btree_trans *trans,
+static int lookup_dirent_in_snapshot(struct btree_trans *trans,
                           struct bch_hash_info hash_info,
                           subvol_inum dir, struct qstr *name,
-                          u64 *target, unsigned *type)
+                          u64 *target, unsigned *type, u32 snapshot)
 {
        struct btree_iter iter;
        struct bkey_s_c_dirent d;
-       int ret;
-
-       ret = bch2_hash_lookup(trans, &iter, bch2_dirent_hash_desc,
-                              &hash_info, dir, name, 0);
+       int ret = bch2_hash_lookup_in_snapshot(trans, &iter, bch2_dirent_hash_desc,
+                              &hash_info, dir, name, 0, snapshot);
        if (ret)
                return ret;
 
@@ -144,34 +142,6 @@ static int __lookup_dirent(struct btree_trans *trans,
        return 0;
 }
 
-static int __write_inode(struct btree_trans *trans,
-                        struct bch_inode_unpacked *inode,
-                        u32 snapshot)
-{
-       struct bkey_inode_buf *inode_p =
-               bch2_trans_kmalloc(trans, sizeof(*inode_p));
-
-       if (IS_ERR(inode_p))
-               return PTR_ERR(inode_p);
-
-       bch2_inode_pack(inode_p, inode);
-       inode_p->inode.k.p.snapshot = snapshot;
-
-       return bch2_btree_insert_nonextent(trans, BTREE_ID_inodes,
-                               &inode_p->inode.k_i,
-                               BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
-}
-
-static int fsck_write_inode(struct btree_trans *trans,
-                           struct bch_inode_unpacked *inode,
-                           u32 snapshot)
-{
-       int ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
-                           __write_inode(trans, inode, snapshot));
-       bch_err_fn(trans->c, ret);
-       return ret;
-}
-
 static int __remove_dirent(struct btree_trans *trans, struct bpos pos)
 {
        struct bch_fs *c = trans->c;
@@ -224,15 +194,16 @@ static int lookup_lostfound(struct btree_trans *trans, u32 snapshot,
 
        struct bch_inode_unpacked root_inode;
        struct bch_hash_info root_hash_info;
-       ret = lookup_inode(trans, root_inum.inum, &root_inode, &snapshot);
+       u32 root_inode_snapshot = snapshot;
+       ret = lookup_inode(trans, root_inum.inum, &root_inode, &root_inode_snapshot);
        bch_err_msg(c, ret, "looking up root inode");
        if (ret)
                return ret;
 
        root_hash_info = bch2_hash_info_init(c, &root_inode);
 
-       ret = __lookup_dirent(trans, root_hash_info, root_inum,
-                             &lostfound_str, &inum, &d_type);
+       ret = lookup_dirent_in_snapshot(trans, root_hash_info, root_inum,
+                             &lostfound_str, &inum, &d_type, snapshot);
        if (bch2_err_matches(ret, ENOENT))
                goto create_lostfound;
 
@@ -250,7 +221,8 @@ static int lookup_lostfound(struct btree_trans *trans, u32 snapshot,
         * shouldn't exist here:
         */
        ret = lookup_inode(trans, inum, lostfound, &snapshot);
-       bch_err_msg(c, ret, "looking up lost+found");
+       bch_err_msg(c, ret, "looking up lost+found %llu:%u in (root inode %llu, snapshot root %u)",
+                   inum, snapshot, root_inum.inum, bch2_snapshot_root(c, snapshot));
        return ret;
 
 create_lostfound:
@@ -312,7 +284,7 @@ static int reattach_inode(struct btree_trans *trans,
        if (S_ISDIR(inode->bi_mode)) {
                lostfound.bi_nlink++;
 
-               ret = __write_inode(trans, &lostfound, U32_MAX);
+               ret = __bch2_fsck_write_inode(trans, &lostfound, U32_MAX);
                if (ret)
                        return ret;
        }
@@ -334,7 +306,7 @@ static int reattach_inode(struct btree_trans *trans,
        inode->bi_dir           = lostfound.bi_inum;
        inode->bi_dir_offset    = dir_offset;
 
-       return __write_inode(trans, inode, inode_snapshot);
+       return __bch2_fsck_write_inode(trans, inode, inode_snapshot);
 }
 
 static int remove_backpointer(struct btree_trans *trans,
@@ -722,7 +694,7 @@ static int hash_redo_key(struct btree_trans *trans,
        delete->k.p = k_iter->pos;
        return  bch2_btree_iter_traverse(k_iter) ?:
                bch2_trans_update(trans, k_iter, delete, 0) ?:
-               bch2_hash_set_snapshot(trans, desc, hash_info,
+               bch2_hash_set_in_snapshot(trans, desc, hash_info,
                                       (subvol_inum) { 0, k.k->p.inode },
                                       k.k->p.snapshot, tmp,
                                       BCH_HASH_SET_MUST_CREATE,
@@ -861,7 +833,8 @@ static int check_inode(struct btree_trans *trans,
 
                u.bi_flags &= ~BCH_INODE_i_size_dirty|BCH_INODE_unlinked;
 
-               ret = __write_inode(trans, &u, iter->pos.snapshot);
+               ret = __bch2_fsck_write_inode(trans, &u, iter->pos.snapshot);
+
                bch_err_msg(c, ret, "in fsck updating inode");
                if (ret)
                        return ret;
@@ -950,8 +923,33 @@ static int check_inode(struct btree_trans *trans,
                do_update = true;
        }
 
+       if (u.bi_subvol) {
+               struct bch_subvolume s;
+
+               ret = bch2_subvolume_get(trans, u.bi_subvol, false, 0, &s);
+               if (ret && !bch2_err_matches(ret, ENOENT))
+                       goto err;
+
+               if (fsck_err_on(ret,
+                               c, inode_bi_subvol_missing,
+                               "inode %llu:%u bi_subvol points to missing subvolume %u",
+                               u.bi_inum, k.k->p.snapshot, u.bi_subvol) ||
+                   fsck_err_on(le64_to_cpu(s.inode) != u.bi_inum ||
+                               !bch2_snapshot_is_ancestor(c, le32_to_cpu(s.snapshot),
+                                                          k.k->p.snapshot),
+                               c, inode_bi_subvol_wrong,
+                               "inode %llu:%u points to subvol %u, but subvol points to %llu:%u",
+                               u.bi_inum, k.k->p.snapshot, u.bi_subvol,
+                               le64_to_cpu(s.inode),
+                               le32_to_cpu(s.snapshot))) {
+                       u.bi_subvol = 0;
+                       u.bi_parent_subvol = 0;
+                       do_update = true;
+               }
+       }
+
        if (do_update) {
-               ret = __write_inode(trans, &u, iter->pos.snapshot);
+               ret = __bch2_fsck_write_inode(trans, &u, iter->pos.snapshot);
                bch_err_msg(c, ret, "in fsck updating inode");
                if (ret)
                        return ret;
@@ -1032,7 +1030,7 @@ static int check_i_sectors(struct btree_trans *trans, struct inode_walker *w)
                                w->last_pos.inode, i->snapshot,
                                i->inode.bi_sectors, i->count)) {
                        i->inode.bi_sectors = i->count;
-                       ret = fsck_write_inode(trans, &i->inode, i->snapshot);
+                       ret = bch2_fsck_write_inode(trans, &i->inode, i->snapshot);
                        if (ret)
                                break;
                }
@@ -1481,7 +1479,7 @@ static int check_subdir_count(struct btree_trans *trans, struct inode_walker *w)
                                "directory %llu:%u with wrong i_nlink: got %u, should be %llu",
                                w->last_pos.inode, i->snapshot, i->inode.bi_nlink, i->count)) {
                        i->inode.bi_nlink = i->count;
-                       ret = fsck_write_inode(trans, &i->inode, i->snapshot);
+                       ret = bch2_fsck_write_inode(trans, &i->inode, i->snapshot);
                        if (ret)
                                break;
                }
@@ -1491,16 +1489,15 @@ fsck_err:
        return ret ?: trans_was_restarted(trans, restart_count);
 }
 
-static int check_dirent_target(struct btree_trans *trans,
-                              struct btree_iter *iter,
-                              struct bkey_s_c_dirent d,
-                              struct bch_inode_unpacked *target,
-                              u32 target_snapshot)
+static int check_inode_backpointer(struct btree_trans *trans,
+                                  struct btree_iter *iter,
+                                  struct bkey_s_c_dirent d,
+                                  struct bch_inode_unpacked *target,
+                                  u32 target_snapshot)
 {
        struct bch_fs *c = trans->c;
-       struct bkey_i_dirent *n;
-       struct printbuf buf = PRINTBUF;
        struct btree_iter bp_iter = { NULL };
+       struct printbuf buf = PRINTBUF;
        int ret = 0;
 
        if (!target->bi_dir &&
@@ -1508,7 +1505,7 @@ static int check_dirent_target(struct btree_trans *trans,
                target->bi_dir          = d.k->p.inode;
                target->bi_dir_offset   = d.k->p.offset;
 
-               ret = __write_inode(trans, target, target_snapshot);
+               ret = __bch2_fsck_write_inode(trans, target, target_snapshot);
                if (ret)
                        goto err;
        }
@@ -1548,7 +1545,7 @@ static int check_dirent_target(struct btree_trans *trans,
                        target->bi_nlink++;
                        target->bi_flags &= ~BCH_INODE_unlinked;
 
-                       ret = __write_inode(trans, target, target_snapshot);
+                       ret = __bch2_fsck_write_inode(trans, target, target_snapshot);
                        if (ret)
                                goto err;
                }
@@ -1566,11 +1563,34 @@ static int check_dirent_target(struct btree_trans *trans,
                        target->bi_dir          = d.k->p.inode;
                        target->bi_dir_offset   = d.k->p.offset;
 
-                       ret = __write_inode(trans, target, target_snapshot);
+                       ret = __bch2_fsck_write_inode(trans, target, target_snapshot);
                        if (ret)
                                goto err;
                }
        }
+out:
+err:
+fsck_err:
+       bch2_trans_iter_exit(trans, &bp_iter);
+       printbuf_exit(&buf);
+       bch_err_fn(c, ret);
+       return ret;
+}
+
+static int check_dirent_target(struct btree_trans *trans,
+                              struct btree_iter *iter,
+                              struct bkey_s_c_dirent d,
+                              struct bch_inode_unpacked *target,
+                              u32 target_snapshot)
+{
+       struct bch_fs *c = trans->c;
+       struct bkey_i_dirent *n;
+       struct printbuf buf = PRINTBUF;
+       int ret = 0;
+
+       ret = check_inode_backpointer(trans, iter, d, target, target_snapshot);
+       if (ret)
+               goto err;
 
        if (fsck_err_on(d.v->d_type != inode_d_type(target),
                        c, dirent_d_type_wrong,
@@ -1614,15 +1634,65 @@ static int check_dirent_target(struct btree_trans *trans,
 
                d = dirent_i_to_s_c(n);
        }
-out:
 err:
 fsck_err:
-       bch2_trans_iter_exit(trans, &bp_iter);
        printbuf_exit(&buf);
        bch_err_fn(c, ret);
        return ret;
 }
 
+static int check_subvol_dirent(struct btree_trans *trans, struct btree_iter *iter,
+                              struct bkey_s_c_dirent d)
+{
+       struct bch_fs *c = trans->c;
+       struct bch_inode_unpacked subvol_root;
+       u32 target_subvol = le32_to_cpu(d.v->d_child_subvol);
+       u32 target_snapshot;
+       u64 target_inum;
+       int ret = 0;
+
+       ret = subvol_lookup(trans, target_subvol,
+                             &target_snapshot, &target_inum);
+       if (ret && !bch2_err_matches(ret, ENOENT))
+               return ret;
+
+       if (fsck_err_on(ret, c, dirent_to_missing_subvol,
+                       "dirent points to missing subvolume %u",
+                       le32_to_cpu(d.v->d_child_subvol)))
+               return __remove_dirent(trans, d.k->p);
+
+       ret = lookup_inode(trans, target_inum,
+                          &subvol_root, &target_snapshot);
+       if (ret && !bch2_err_matches(ret, ENOENT))
+               return ret;
+
+       if (fsck_err_on(ret, c, subvol_to_missing_root,
+                       "subvolume %u points to missing subvolume root %llu",
+                       target_subvol,
+                       target_inum)) {
+               bch_err(c, "repair not implemented yet");
+               return -EINVAL;
+       }
+
+       if (fsck_err_on(subvol_root.bi_subvol != target_subvol,
+                       c, subvol_root_wrong_bi_subvol,
+                       "subvol root %llu has wrong bi_subvol field: got %u, should be %u",
+                       target_inum,
+                       subvol_root.bi_subvol, target_subvol)) {
+               subvol_root.bi_subvol = target_subvol;
+               ret = __bch2_fsck_write_inode(trans, &subvol_root, target_snapshot);
+               if (ret)
+                       return ret;
+       }
+
+       ret = check_dirent_target(trans, iter, d, &subvol_root,
+                                 target_snapshot);
+       if (ret)
+               return ret;
+fsck_err:
+       return ret;
+}
+
 static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                        struct bkey_s_c k,
                        struct bch_hash_info *hash_info,
@@ -1707,50 +1777,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
        d = bkey_s_c_to_dirent(k);
 
        if (d.v->d_type == DT_SUBVOL) {
-               struct bch_inode_unpacked subvol_root;
-               u32 target_subvol = le32_to_cpu(d.v->d_child_subvol);
-               u32 target_snapshot;
-               u64 target_inum;
-
-               ret = subvol_lookup(trans, target_subvol,
-                                     &target_snapshot, &target_inum);
-               if (ret && !bch2_err_matches(ret, ENOENT))
-                       goto err;
-
-               if (fsck_err_on(ret, c, dirent_to_missing_subvol,
-                               "dirent points to missing subvolume %u",
-                               le32_to_cpu(d.v->d_child_subvol))) {
-                       ret = __remove_dirent(trans, d.k->p);
-                       goto err;
-               }
-
-               ret = lookup_inode(trans, target_inum,
-                                  &subvol_root, &target_snapshot);
-               if (ret && !bch2_err_matches(ret, ENOENT))
-                       goto err;
-
-               if (fsck_err_on(ret, c, subvol_to_missing_root,
-                               "subvolume %u points to missing subvolume root %llu",
-                               target_subvol,
-                               target_inum)) {
-                       bch_err(c, "repair not implemented yet");
-                       ret = -EINVAL;
-                       goto err;
-               }
-
-               if (fsck_err_on(subvol_root.bi_subvol != target_subvol,
-                               c, subvol_root_wrong_bi_subvol,
-                               "subvol root %llu has wrong bi_subvol field: got %u, should be %u",
-                               target_inum,
-                               subvol_root.bi_subvol, target_subvol)) {
-                       subvol_root.bi_subvol = target_subvol;
-                       ret = __write_inode(trans, &subvol_root, target_snapshot);
-                       if (ret)
-                               goto err;
-               }
-
-               ret = check_dirent_target(trans, iter, d, &subvol_root,
-                                         target_snapshot);
+               ret = check_subvol_dirent(trans, iter, d);
                if (ret)
                        goto err;
        } else {
@@ -1776,12 +1803,11 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                        if (ret)
                                goto err;
                }
-       }
-
-       if (d.v->d_type == DT_DIR)
-               for_each_visible_inode(c, s, dir, equiv.snapshot, i)
-                       i->count++;
 
+               if (d.v->d_type == DT_DIR)
+                       for_each_visible_inode(c, s, dir, equiv.snapshot, i)
+                               i->count++;
+       }
 out:
 err:
 fsck_err:
@@ -1919,7 +1945,7 @@ static int check_root_trans(struct btree_trans *trans)
                                0, NULL);
                root_inode.bi_inum = inum;
 
-               ret = __write_inode(trans, &root_inode, snapshot);
+               ret = __bch2_fsck_write_inode(trans, &root_inode, snapshot);
                bch_err_msg(c, ret, "writing root inode");
        }
 err:
@@ -2291,7 +2317,7 @@ static int check_nlinks_update_inode(struct btree_trans *trans, struct btree_ite
                        u.bi_inum, bch2_d_types[mode_to_type(u.bi_mode)],
                        bch2_inode_nlink_get(&u), link->count)) {
                bch2_inode_nlink_set(&u, link->count);
-               ret = __write_inode(trans, &u, k.k->p.snapshot);
+               ret = __bch2_fsck_write_inode(trans, &u, k.k->p.snapshot);
        }
 fsck_err:
        return ret;