bcachefs: Add support for dirents that point to subvolumes
authorKent Overstreet <kent.overstreet@gmail.com>
Tue, 16 Mar 2021 04:46:26 +0000 (00:46 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:12 +0000 (17:09 -0400)
Dirents currently always point to inodes. Subvolumes add a new type of
dirent, with d_type DT_SUBVOL, that instead points to an entry in the
subvolumes btree, and the subvolume has a pointer to the root inode.

This patch adds bch2_dirent_read_target() to get the inode (and
potentially subvolume) a dirent points to, and changes existing code to
use that instead of reading from d_inum directly.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
fs/bcachefs/dirent.c
fs/bcachefs/dirent.h
fs/bcachefs/fs-common.c
fs/bcachefs/fsck.c

index 53c7687a9ca898e88dfa045781e8cb6d1a228e3f..f3aef06869283b430fefd241507cbffc6d1cba81 100644 (file)
@@ -177,6 +177,61 @@ static void dirent_copy_target(struct bkey_i_dirent *dst,
        dst->v.d_type = src.v->d_type;
 }
 
+int __bch2_dirent_read_target(struct btree_trans *trans,
+                             struct bkey_s_c_dirent d,
+                             u32 *subvol, u32 *snapshot, u64 *inum,
+                             bool is_fsck)
+{
+       int ret = 0;
+
+       *subvol         = 0;
+       *snapshot       = d.k->p.snapshot;
+
+       if (likely(d.v->d_type != DT_SUBVOL)) {
+               *inum = le64_to_cpu(d.v->d_inum);
+       } else {
+               struct btree_iter iter;
+               struct bkey_s_c k;
+               struct bkey_s_c_subvolume s;
+               int ret;
+
+               *subvol = le64_to_cpu(d.v->d_inum);
+               bch2_trans_iter_init(trans, &iter, BTREE_ID_subvolumes,
+                                    POS(0, *subvol),
+                                    BTREE_ITER_CACHED);
+               k = bch2_btree_iter_peek_slot(&iter);
+               ret = bkey_err(k);
+               if (ret)
+                       goto err;
+
+               if (k.k->type != KEY_TYPE_subvolume) {
+                       ret = -ENOENT;
+                       goto err;
+               }
+
+               s = bkey_s_c_to_subvolume(k);
+               *snapshot       = le32_to_cpu(s.v->snapshot);
+               *inum           = le64_to_cpu(s.v->inode);
+err:
+               if (ret == -ENOENT && !is_fsck)
+                       bch2_fs_inconsistent(trans->c, "pointer to missing subvolume %u",
+                                            *subvol);
+
+               bch2_trans_iter_exit(trans, &iter);
+       }
+
+       return ret;
+}
+
+int bch2_dirent_read_target(struct btree_trans *trans,
+                           struct bkey_s_c_dirent d, u64 *target)
+{
+       u32 subvol, snapshot;
+
+       return __bch2_dirent_read_target(trans, d, &subvol,
+                                        &snapshot, target, false);
+}
+
 int bch2_dirent_rename(struct btree_trans *trans,
                       u64 src_dir, struct bch_hash_info *src_hash,
                       u64 dst_dir, struct bch_hash_info *dst_hash,
@@ -323,10 +378,32 @@ int __bch2_dirent_lookup_trans(struct btree_trans *trans,
                               struct btree_iter *iter,
                               u64 dir_inum,
                               const struct bch_hash_info *hash_info,
-                              const struct qstr *name, unsigned flags)
+                              const struct qstr *name, u64 *inum,
+                              unsigned flags)
 {
-       return bch2_hash_lookup(trans, iter, bch2_dirent_hash_desc,
-                               hash_info, dir_inum, name, flags);
+       struct bkey_s_c k;
+       struct bkey_s_c_dirent d;
+       int ret;
+
+       ret = bch2_hash_lookup(trans, iter, bch2_dirent_hash_desc,
+                              hash_info, dir_inum, name, flags);
+       if (ret)
+               return ret;
+
+       k = bch2_btree_iter_peek_slot(iter);
+       ret = bkey_err(k);
+       if (ret) {
+               bch2_trans_iter_exit(trans, iter);
+               return ret;
+       }
+
+       d = bkey_s_c_to_dirent(k);
+
+       ret = bch2_dirent_read_target(trans, d, inum);
+       if (ret)
+               bch2_trans_iter_exit(trans, iter);
+
+       return ret;
 }
 
 u64 bch2_dirent_lookup(struct bch_fs *c, u64 dir_inum,
@@ -335,26 +412,18 @@ u64 bch2_dirent_lookup(struct bch_fs *c, u64 dir_inum,
 {
        struct btree_trans trans;
        struct btree_iter iter;
-       struct bkey_s_c k;
        u64 inum = 0;
        int ret = 0;
 
        bch2_trans_init(&trans, c, 0, 0);
+retry:
+       bch2_trans_begin(&trans);
+       ret = __bch2_dirent_lookup_trans(&trans, &iter, dir_inum, hash_info,
+                                        name, &inum, 0);
 
-       ret = __bch2_dirent_lookup_trans(&trans, &iter, dir_inum,
-                                        hash_info, name, 0);
-       if (ret)
-               goto out;
-
-       k = bch2_btree_iter_peek_slot(&iter);
-       ret = bkey_err(k);
-       if (ret)
-               goto out;
-
-       inum = le64_to_cpu(bkey_s_c_to_dirent(k).v->d_inum);
        bch2_trans_iter_exit(&trans, &iter);
-out:
-       BUG_ON(ret == -EINTR);
+       if (ret == -EINTR)
+               goto retry;
        bch2_trans_exit(&trans);
        return inum;
 }
@@ -408,7 +477,7 @@ int bch2_readdir(struct bch_fs *c, u64 inum, struct dir_context *ctx)
                if (!dir_emit(ctx, dirent.v->d_name,
                              bch2_dirent_name_bytes(dirent),
                              le64_to_cpu(dirent.v->d_inum),
-                             dirent.v->d_type))
+                             vfs_d_type(dirent.v->d_type)))
                        break;
                ctx->pos = dirent.k->p.offset + 1;
        }
index c14f6029e1c98b7064440eab94564079f00a11bf..3cd05a2454e11e47febb75771115bc2e5dc0fe89 100644 (file)
@@ -37,6 +37,17 @@ int bch2_dirent_delete_at(struct btree_trans *,
                          const struct bch_hash_info *,
                          struct btree_iter *);
 
+int __bch2_dirent_read_target(struct btree_trans *, struct bkey_s_c_dirent,
+                             u32 *, u32 *, u64 *, bool);
+
+int bch2_dirent_read_target(struct btree_trans *,
+                           struct bkey_s_c_dirent, u64 *);
+
+static inline unsigned vfs_d_type(unsigned type)
+{
+       return type == DT_SUBVOL ? DT_DIR : type;
+}
+
 enum bch_rename_mode {
        BCH_RENAME,
        BCH_RENAME_OVERWRITE,
@@ -52,7 +63,8 @@ int bch2_dirent_rename(struct btree_trans *,
 
 int __bch2_dirent_lookup_trans(struct btree_trans *, struct btree_iter *, u64,
                           const struct bch_hash_info *,
-                          const struct qstr *, unsigned);
+                          const struct qstr *, u64 *,
+                          unsigned);
 u64 bch2_dirent_lookup(struct bch_fs *, u64, const struct bch_hash_info *,
                       const struct qstr *);
 
index 6bc82559c9b17e64866a3f11a74d9e246320b011..96b09b005d0bcc54720f887a5404e255b9409b82 100644 (file)
@@ -159,17 +159,10 @@ int bch2_unlink_trans(struct btree_trans *trans,
        dir_hash = bch2_hash_info_init(c, dir_u);
 
        ret = __bch2_dirent_lookup_trans(trans, &dirent_iter, dir_inum, &dir_hash,
-                                        name, BTREE_ITER_INTENT);
+                                        name, &inum, BTREE_ITER_INTENT);
        if (ret)
                goto err;
 
-       k = bch2_btree_iter_peek_slot(&dirent_iter);
-       ret = bkey_err(k);
-       if (ret)
-               goto err;
-
-       inum = le64_to_cpu(bkey_s_c_to_dirent(k).v->d_inum);
-
        ret = bch2_inode_peek(trans, &inode_iter, inode_u, inum, BTREE_ITER_INTENT);
        if (ret)
                goto err;
index 62158c0803db580fa7e4c6848883489fc1914eb3..dca4abda2c413719e1b5ab668e3a03a42a04bd32 100644 (file)
@@ -723,6 +723,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
        struct bkey_s_c_dirent d;
        struct bch_inode_unpacked target;
        u32 target_snapshot;
+       u32 target_subvol;
        bool have_target;
        bool backpointer_exists = true;
        u64 d_inum;
@@ -783,6 +784,10 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
        d = bkey_s_c_to_dirent(k);
        d_inum = le64_to_cpu(d.v->d_inum);
 
+       ret = bch2_dirent_read_target(trans, d, &d_inum);
+       if (ret && ret != -ENOENT)
+               return ret;
+
        ret = __lookup_inode(trans, d_inum, &target, &target_snapshot);
        if (ret && ret != -ENOENT)
                return ret;
@@ -855,7 +860,23 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                }
        }
 
-       if (fsck_err_on(d.v->d_type != mode_to_type(target.bi_mode), c,
+       target_subvol = d.v->d_type == DT_SUBVOL
+               ? le64_to_cpu(d.v->d_inum) : 0;
+
+       if (fsck_err_on(target.bi_subvol != target_subvol, c,
+                       "subvol root %llu has wrong subvol field:\n"
+                       "got       %u\n"
+                       "should be %u",
+                       target.bi_inum,
+                       target.bi_subvol,
+                       target_subvol)) {
+               target.bi_subvol = target_subvol;
+
+               ret = write_inode(trans, &target, target_snapshot);
+               return ret ?: -EINTR;
+       }
+
+       if (fsck_err_on(vfs_d_type(d.v->d_type) != mode_to_type(target.bi_mode), c,
                        "incorrect d_type: should be %u:\n%s",
                        mode_to_type(target.bi_mode),
                        (bch2_bkey_val_to_text(&PBUF(buf), c,