From b9e1adf57988fb4632b86c43fde1551a24299b86 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Tue, 16 Mar 2021 00:46:26 -0400 Subject: [PATCH] bcachefs: Add support for dirents that point to subvolumes 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 --- fs/bcachefs/dirent.c | 105 +++++++++++++++++++++++++++++++++------- fs/bcachefs/dirent.h | 14 +++++- fs/bcachefs/fs-common.c | 9 +--- fs/bcachefs/fsck.c | 23 ++++++++- 4 files changed, 123 insertions(+), 28 deletions(-) diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c index 53c7687a9ca89..f3aef06869283 100644 --- a/fs/bcachefs/dirent.c +++ b/fs/bcachefs/dirent.c @@ -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; } diff --git a/fs/bcachefs/dirent.h b/fs/bcachefs/dirent.h index c14f6029e1c98..3cd05a2454e11 100644 --- a/fs/bcachefs/dirent.h +++ b/fs/bcachefs/dirent.h @@ -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 *); diff --git a/fs/bcachefs/fs-common.c b/fs/bcachefs/fs-common.c index 6bc82559c9b17..96b09b005d0bc 100644 --- a/fs/bcachefs/fs-common.c +++ b/fs/bcachefs/fs-common.c @@ -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; diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 62158c0803db5..dca4abda2c413 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -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, -- 2.30.2