summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2021-03-16 00:46:26 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2021-04-27 17:05:13 -0400
commit15cab6dd55e4022b1dc59ba528bb5ff23659232d (patch)
treeaa75b1fa6046fb056248c009193ce5a3dda9df47
parent8903ee57fcb9ae948021121d2df1c006ec648f97 (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.c87
-rw-r--r--fs/bcachefs/dirent.h11
-rw-r--r--fs/bcachefs/fs-common.c5
-rw-r--r--fs/bcachefs/fsck.c25
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,