bcachefs: bch_subvolume::fs_path_parent
Record the filesystem path heirarchy for subvolumes in bch_subvolume Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
e07c28ab92
commit
b8628a2529
@ -840,7 +840,8 @@ struct bch_sb_field_downgrade {
|
|||||||
x(snapshot_skiplists, BCH_VERSION(1, 1)) \
|
x(snapshot_skiplists, BCH_VERSION(1, 1)) \
|
||||||
x(deleted_inodes, BCH_VERSION(1, 2)) \
|
x(deleted_inodes, BCH_VERSION(1, 2)) \
|
||||||
x(rebalance_work, BCH_VERSION(1, 3)) \
|
x(rebalance_work, BCH_VERSION(1, 3)) \
|
||||||
x(member_seq, BCH_VERSION(1, 4))
|
x(member_seq, BCH_VERSION(1, 4)) \
|
||||||
|
x(subvolume_fs_parent, BCH_VERSION(1, 5))
|
||||||
|
|
||||||
enum bcachefs_metadata_version {
|
enum bcachefs_metadata_version {
|
||||||
bcachefs_metadata_version_min = 9,
|
bcachefs_metadata_version_min = 9,
|
||||||
|
|||||||
@ -107,6 +107,7 @@ int bch2_create_trans(struct btree_trans *trans,
|
|||||||
u32 new_subvol, dir_snapshot;
|
u32 new_subvol, dir_snapshot;
|
||||||
|
|
||||||
ret = bch2_subvolume_create(trans, new_inode->bi_inum,
|
ret = bch2_subvolume_create(trans, new_inode->bi_inum,
|
||||||
|
dir.subvol,
|
||||||
snapshot_src.subvol,
|
snapshot_src.subvol,
|
||||||
&new_subvol, &snapshot,
|
&new_subvol, &snapshot,
|
||||||
(flags & BCH_CREATE_SNAPSHOT_RO) != 0);
|
(flags & BCH_CREATE_SNAPSHOT_RO) != 0);
|
||||||
@ -349,6 +350,22 @@ bool bch2_reinherit_attrs(struct bch_inode_unpacked *dst_u,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int subvol_update_parent(struct btree_trans *trans, u32 subvol, u32 new_parent)
|
||||||
|
{
|
||||||
|
struct btree_iter iter;
|
||||||
|
struct bkey_i_subvolume *s =
|
||||||
|
bch2_bkey_get_mut_typed(trans, &iter,
|
||||||
|
BTREE_ID_subvolumes, POS(0, subvol),
|
||||||
|
BTREE_ITER_CACHED, subvolume);
|
||||||
|
int ret = PTR_ERR_OR_ZERO(s);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
s->v.fs_path_parent = cpu_to_le32(new_parent);
|
||||||
|
bch2_trans_iter_exit(trans, &iter);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int bch2_rename_trans(struct btree_trans *trans,
|
int bch2_rename_trans(struct btree_trans *trans,
|
||||||
subvol_inum src_dir, struct bch_inode_unpacked *src_dir_u,
|
subvol_inum src_dir, struct bch_inode_unpacked *src_dir_u,
|
||||||
subvol_inum dst_dir, struct bch_inode_unpacked *dst_dir_u,
|
subvol_inum dst_dir, struct bch_inode_unpacked *dst_dir_u,
|
||||||
@ -410,6 +427,21 @@ int bch2_rename_trans(struct btree_trans *trans,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src_inode_u->bi_subvol &&
|
||||||
|
dst_dir.subvol != src_inode_u->bi_parent_subvol) {
|
||||||
|
ret = subvol_update_parent(trans, src_inode_u->bi_subvol, dst_dir.subvol);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == BCH_RENAME_EXCHANGE &&
|
||||||
|
dst_inode_u->bi_subvol &&
|
||||||
|
src_dir.subvol != dst_inode_u->bi_parent_subvol) {
|
||||||
|
ret = subvol_update_parent(trans, dst_inode_u->bi_subvol, src_dir.subvol);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Can't move across subvolumes, unless it's a subvolume root: */
|
/* Can't move across subvolumes, unless it's a subvolume root: */
|
||||||
if (src_dir.subvol != dst_dir.subvol &&
|
if (src_dir.subvol != dst_dir.subvol &&
|
||||||
(!src_inode_u->bi_subvol ||
|
(!src_inode_u->bi_subvol ||
|
||||||
|
|||||||
@ -1744,11 +1744,12 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
|
|||||||
struct bkey_s_c_dirent d)
|
struct bkey_s_c_dirent d)
|
||||||
{
|
{
|
||||||
struct bch_fs *c = trans->c;
|
struct bch_fs *c = trans->c;
|
||||||
|
struct btree_iter subvol_iter = {};
|
||||||
struct bch_inode_unpacked subvol_root;
|
struct bch_inode_unpacked subvol_root;
|
||||||
u32 parent_subvol = le32_to_cpu(d.v->d_parent_subvol);
|
u32 parent_subvol = le32_to_cpu(d.v->d_parent_subvol);
|
||||||
u32 target_subvol = le32_to_cpu(d.v->d_child_subvol);
|
u32 target_subvol = le32_to_cpu(d.v->d_child_subvol);
|
||||||
u32 target_snapshot, parent_snapshot;
|
u32 parent_snapshot;
|
||||||
u64 target_inum, parent_inum;
|
u64 parent_inum;
|
||||||
struct printbuf buf = PRINTBUF;
|
struct printbuf buf = PRINTBUF;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -1777,8 +1778,11 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
|
|||||||
new_dirent->v.d_parent_subvol = cpu_to_le32(new_parent_subvol);
|
new_dirent->v.d_parent_subvol = cpu_to_le32(new_parent_subvol);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = subvol_lookup(trans, target_subvol,
|
struct bkey_s_c_subvolume s =
|
||||||
&target_snapshot, &target_inum);
|
bch2_bkey_get_iter_typed(trans, &subvol_iter,
|
||||||
|
BTREE_ID_subvolumes, POS(0, target_subvol),
|
||||||
|
0, subvolume);
|
||||||
|
ret = bkey_err(s.s_c);
|
||||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1791,8 +1795,24 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = lookup_inode(trans, target_inum,
|
if (fsck_err_on(le32_to_cpu(s.v->fs_path_parent) != parent_subvol,
|
||||||
&subvol_root, &target_snapshot);
|
c, subvol_fs_path_parent_wrong,
|
||||||
|
"subvol with wrong fs_path_parent, should be be %u\n%s",
|
||||||
|
parent_subvol,
|
||||||
|
(bch2_bkey_val_to_text(&buf, c, s.s_c), buf.buf))) {
|
||||||
|
struct bkey_i_subvolume *n =
|
||||||
|
bch2_bkey_make_mut_typed(trans, &subvol_iter, &s.s_c, 0, subvolume);
|
||||||
|
ret = PTR_ERR_OR_ZERO(n);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
n->v.fs_path_parent = cpu_to_le32(parent_subvol);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 target_inum = le64_to_cpu(s.v->inode);
|
||||||
|
u32 target_snapshot = le32_to_cpu(s.v->snapshot);
|
||||||
|
|
||||||
|
ret = lookup_inode(trans, target_inum, &subvol_root, &target_snapshot);
|
||||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1814,6 +1834,7 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
|
|||||||
out:
|
out:
|
||||||
err:
|
err:
|
||||||
fsck_err:
|
fsck_err:
|
||||||
|
bch2_trans_iter_exit(trans, &subvol_iter);
|
||||||
printbuf_exit(&buf);
|
printbuf_exit(&buf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,10 @@
|
|||||||
BIT_ULL(BCH_RECOVERY_PASS_check_inodes), \
|
BIT_ULL(BCH_RECOVERY_PASS_check_inodes), \
|
||||||
BCH_FSCK_ERR_unlinked_inode_not_on_deleted_list) \
|
BCH_FSCK_ERR_unlinked_inode_not_on_deleted_list) \
|
||||||
x(rebalance_work, \
|
x(rebalance_work, \
|
||||||
BIT_ULL(BCH_RECOVERY_PASS_set_fs_needs_rebalance))
|
BIT_ULL(BCH_RECOVERY_PASS_set_fs_needs_rebalance)) \
|
||||||
|
x(subvolume_fs_parent, \
|
||||||
|
BIT_ULL(BCH_RECOVERY_PASS_check_dirents), \
|
||||||
|
BCH_FSCK_ERR_subvol_fs_path_parent_wrong)
|
||||||
|
|
||||||
#define DOWNGRADE_TABLE()
|
#define DOWNGRADE_TABLE()
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ static int check_subvol(struct btree_trans *trans,
|
|||||||
struct bch_fs *c = trans->c;
|
struct bch_fs *c = trans->c;
|
||||||
struct bkey_s_c_subvolume subvol;
|
struct bkey_s_c_subvolume subvol;
|
||||||
struct bch_snapshot snapshot;
|
struct bch_snapshot snapshot;
|
||||||
|
struct printbuf buf = PRINTBUF;
|
||||||
unsigned snapid;
|
unsigned snapid;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -42,6 +43,20 @@ static int check_subvol(struct btree_trans *trans,
|
|||||||
return ret ?: -BCH_ERR_transaction_restart_nested;
|
return ret ?: -BCH_ERR_transaction_restart_nested;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fsck_err_on(subvol.k->p.offset == BCACHEFS_ROOT_SUBVOL &&
|
||||||
|
subvol.v->fs_path_parent,
|
||||||
|
c, subvol_root_fs_path_parent_nonzero,
|
||||||
|
"root subvolume has nonzero fs_path_parent\n%s",
|
||||||
|
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||||
|
struct bkey_i_subvolume *n =
|
||||||
|
bch2_bkey_make_mut_typed(trans, iter, &subvol.s_c, 0, subvolume);
|
||||||
|
ret = PTR_ERR_OR_ZERO(n);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
n->v.fs_path_parent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct bch_inode_unpacked inode;
|
struct bch_inode_unpacked inode;
|
||||||
struct btree_iter inode_iter = {};
|
struct btree_iter inode_iter = {};
|
||||||
ret = bch2_inode_peek_nowarn(trans, &inode_iter, &inode,
|
ret = bch2_inode_peek_nowarn(trans, &inode_iter, &inode,
|
||||||
@ -102,9 +117,9 @@ static int check_subvol(struct btree_trans *trans,
|
|||||||
SET_BCH_SUBVOLUME_SNAP(&s->v, true);
|
SET_BCH_SUBVOLUME_SNAP(&s->v, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
fsck_err:
|
fsck_err:
|
||||||
|
printbuf_exit(&buf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +158,10 @@ void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c,
|
|||||||
le64_to_cpu(s.v->inode),
|
le64_to_cpu(s.v->inode),
|
||||||
le32_to_cpu(s.v->snapshot));
|
le32_to_cpu(s.v->snapshot));
|
||||||
|
|
||||||
if (bkey_val_bytes(s.k) > offsetof(struct bch_subvolume, creation_parent))
|
if (bkey_val_bytes(s.k) > offsetof(struct bch_subvolume, creation_parent)) {
|
||||||
prt_printf(out, " creation_parent %u", le32_to_cpu(s.v->creation_parent));
|
prt_printf(out, " creation_parent %u", le32_to_cpu(s.v->creation_parent));
|
||||||
|
prt_printf(out, " fs_parent %u", le32_to_cpu(s.v->fs_path_parent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline int
|
static __always_inline int
|
||||||
@ -391,6 +408,7 @@ int bch2_subvolume_unlink(struct btree_trans *trans, u32 subvolid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int bch2_subvolume_create(struct btree_trans *trans, u64 inode,
|
int bch2_subvolume_create(struct btree_trans *trans, u64 inode,
|
||||||
|
u32 parent_subvolid,
|
||||||
u32 src_subvolid,
|
u32 src_subvolid,
|
||||||
u32 *new_subvolid,
|
u32 *new_subvolid,
|
||||||
u32 *new_snapshotid,
|
u32 *new_snapshotid,
|
||||||
@ -451,6 +469,7 @@ int bch2_subvolume_create(struct btree_trans *trans, u64 inode,
|
|||||||
new_subvol->v.snapshot = cpu_to_le32(new_nodes[0]);
|
new_subvol->v.snapshot = cpu_to_le32(new_nodes[0]);
|
||||||
new_subvol->v.inode = cpu_to_le64(inode);
|
new_subvol->v.inode = cpu_to_le64(inode);
|
||||||
new_subvol->v.creation_parent = cpu_to_le32(src_subvolid);
|
new_subvol->v.creation_parent = cpu_to_le32(src_subvolid);
|
||||||
|
new_subvol->v.fs_path_parent = cpu_to_le32(parent_subvolid);
|
||||||
new_subvol->v.otime.lo = cpu_to_le64(bch2_current_time(c));
|
new_subvol->v.otime.lo = cpu_to_le64(bch2_current_time(c));
|
||||||
new_subvol->v.otime.hi = 0;
|
new_subvol->v.otime.hi = 0;
|
||||||
|
|
||||||
|
|||||||
@ -30,8 +30,7 @@ int bch2_delete_dead_snapshots(struct bch_fs *);
|
|||||||
void bch2_delete_dead_snapshots_async(struct bch_fs *);
|
void bch2_delete_dead_snapshots_async(struct bch_fs *);
|
||||||
|
|
||||||
int bch2_subvolume_unlink(struct btree_trans *, u32);
|
int bch2_subvolume_unlink(struct btree_trans *, u32);
|
||||||
int bch2_subvolume_create(struct btree_trans *, u64, u32,
|
int bch2_subvolume_create(struct btree_trans *, u64, u32, u32, u32 *, u32 *, bool);
|
||||||
u32 *, u32 *, bool);
|
|
||||||
|
|
||||||
int bch2_fs_subvolumes_init(struct bch_fs *);
|
int bch2_fs_subvolumes_init(struct bch_fs *);
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ struct bch_subvolume {
|
|||||||
* this subvolume:
|
* this subvolume:
|
||||||
*/
|
*/
|
||||||
__le32 creation_parent;
|
__le32 creation_parent;
|
||||||
__le32 pad;
|
__le32 fs_path_parent;
|
||||||
bch_le128 otime;
|
bch_le128 otime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user