diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2021-03-16 00:46:26 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2021-04-27 17:05:13 -0400 |
commit | 15cab6dd55e4022b1dc59ba528bb5ff23659232d (patch) | |
tree | aa75b1fa6046fb056248c009193ce5a3dda9df47 | |
parent | 8903ee57fcb9ae948021121d2df1c006ec648f97 (diff) |
bcachefs: Add support for dirents that point to subvolumes
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | fs/bcachefs/dirent.c | 87 | ||||
-rw-r--r-- | fs/bcachefs/dirent.h | 11 | ||||
-rw-r--r-- | fs/bcachefs/fs-common.c | 5 | ||||
-rw-r--r-- | fs/bcachefs/fsck.c | 25 |
4 files changed, 105 insertions, 23 deletions
diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c index c2956c8527fa..e621a4987f29 100644 --- a/fs/bcachefs/dirent.c +++ b/fs/bcachefs/dirent.c @@ -174,6 +174,43 @@ 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, u64 *target) +{ + int ret = 0; + + if (likely(d.v->d_type != DT_SUBVOL)) { + *target = 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; + + iter = bch2_trans_get_iter(trans, BTREE_ID_subvolumes, + POS(0, le64_to_cpu(d.v->d_inum)), 0); + + k = bch2_btree_iter_peek_slot(iter); + ret = bkey_err(k); + if (ret) + goto put_iter; + + if (k.k->type != KEY_TYPE_subvolume) { + bch2_fs_inconsistent(trans->c, "pointer to missing subvolume %llu", + iter->pos.offset); + ret = -ENOENT; + goto put_iter; + } + + s = bkey_s_c_to_subvolume(k); + *target = le64_to_cpu(s.v->inode); +put_iter: + bch2_trans_iter_put(trans, iter); + } + + return ret; +} + 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, @@ -308,12 +345,33 @@ int bch2_dirent_delete_at(struct btree_trans *trans, } struct btree_iter * -__bch2_dirent_lookup_trans(struct btree_trans *trans, u64 dir_inum, +__bch2_dirent_lookup_trans(struct btree_trans *trans, u64 dir, 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, bch2_dirent_hash_desc, - hash_info, dir_inum, name, flags); + struct btree_iter *iter = NULL; + struct bkey_s_c k; + struct bkey_s_c_dirent d; + int ret; + + iter = bch2_hash_lookup(trans, bch2_dirent_hash_desc, + hash_info, dir, name, flags); + if (IS_ERR(iter)) + return iter; + + k = bch2_btree_iter_peek_slot(iter); + BUG_ON(bkey_err(k)); + + d = bkey_s_c_to_dirent(k); + + ret = bch2_dirent_read_target(trans, d, inum); + if (ret) { + bch2_trans_iter_put(trans, iter); + return ERR_PTR(ret); + } + + return iter; } u64 bch2_dirent_lookup(struct bch_fs *c, u64 dir_inum, @@ -322,22 +380,19 @@ 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; bch2_trans_init(&trans, c, 0, 0); +retry: + bch2_trans_begin(&trans); - iter = __bch2_dirent_lookup_trans(&trans, dir_inum, - hash_info, name, 0); - if (IS_ERR(iter)) { - BUG_ON(PTR_ERR(iter) == -EINTR); - goto out; - } - - k = bch2_btree_iter_peek_slot(iter); - inum = le64_to_cpu(bkey_s_c_to_dirent(k).v->d_inum); + iter = __bch2_dirent_lookup_trans(&trans, dir_inum, hash_info, + name, &inum, 0); + ret = PTR_ERR_OR_ZERO(iter); bch2_trans_iter_put(&trans, iter); -out: + if (ret == -EINTR) + goto retry; bch2_trans_exit(&trans); return inum; } @@ -391,7 +446,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 e1d8ce377d43..de70b7706fd5 100644 --- a/fs/bcachefs/dirent.h +++ b/fs/bcachefs/dirent.h @@ -37,6 +37,14 @@ 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, 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, @@ -53,7 +61,8 @@ int bch2_dirent_rename(struct btree_trans *, struct btree_iter * __bch2_dirent_lookup_trans(struct btree_trans *, 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 34d69c3f6680..f3b49945047d 100644 --- a/fs/bcachefs/fs-common.c +++ b/fs/bcachefs/fs-common.c @@ -160,14 +160,11 @@ int bch2_unlink_trans(struct btree_trans *trans, dir_hash = bch2_hash_info_init(c, dir_u); dirent_iter = __bch2_dirent_lookup_trans(trans, dir_inum, &dir_hash, - name, BTREE_ITER_INTENT); + name, &inum, BTREE_ITER_INTENT); ret = PTR_ERR_OR_ZERO(dirent_iter); if (ret) goto err; - k = bch2_btree_iter_peek_slot(dirent_iter); - inum = le64_to_cpu(bkey_s_c_to_dirent(k).v->d_inum); - inode_iter = bch2_inode_peek(trans, inode_u, inum, BTREE_ITER_INTENT); ret = PTR_ERR_OR_ZERO(inode_iter); if (ret) diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 29de4828c0f8..922c9d360be0 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -727,6 +727,7 @@ retry: 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; @@ -784,7 +785,10 @@ retry: goto next; d = bkey_s_c_to_dirent(k); - d_inum = le64_to_cpu(d.v->d_inum); + + ret = lockrestart_do(&trans, bch2_dirent_read_target(&trans, d, &d_inum)); + if (ret && ret != -ENOENT) + break; ret = lookup_inode(&trans, d_inum, &target, &target_snapshot); if (ret && ret != -ENOENT) @@ -864,7 +868,24 @@ retry: } } - 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); + if (ret) + goto err; + } + + 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, |