bcachefs: bch2_check_subvolume_structure()
Now that we've got bch_subvolume.fs_path_parent, it's easy to write subvolume Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
b07ce72626
commit
663db5a554
@ -342,6 +342,27 @@ static int remove_backpointer(struct btree_trans *trans,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reattach_subvol(struct btree_trans *trans, struct bkey_s_c_subvolume s)
|
||||||
|
{
|
||||||
|
struct bch_fs *c = trans->c;
|
||||||
|
|
||||||
|
struct bch_inode_unpacked inode;
|
||||||
|
int ret = bch2_inode_find_by_inum_trans(trans,
|
||||||
|
(subvol_inum) { s.k->p.offset, le64_to_cpu(s.v->inode) },
|
||||||
|
&inode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = remove_backpointer(trans, &inode);
|
||||||
|
bch_err_msg(c, ret, "removing dirent");
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = reattach_inode(trans, &inode, le32_to_cpu(s.v->snapshot));
|
||||||
|
bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct snapshots_seen_entry {
|
struct snapshots_seen_entry {
|
||||||
u32 id;
|
u32 id;
|
||||||
u32 equiv;
|
u32 equiv;
|
||||||
@ -2108,6 +2129,107 @@ int bch2_check_root(struct bch_fs *c)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef DARRAY(u32) darray_u32;
|
||||||
|
|
||||||
|
static bool darray_u32_has(darray_u32 *d, u32 v)
|
||||||
|
{
|
||||||
|
darray_for_each(*d, i)
|
||||||
|
if (*i == v)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We've checked that inode backpointers point to valid dirents; here, it's
|
||||||
|
* sufficient to check that the subvolume root has a dirent:
|
||||||
|
*/
|
||||||
|
static int subvol_has_dirent(struct btree_trans *trans, struct bkey_s_c_subvolume s)
|
||||||
|
{
|
||||||
|
struct bch_inode_unpacked inode;
|
||||||
|
int ret = bch2_inode_find_by_inum_trans(trans,
|
||||||
|
(subvol_inum) { s.k->p.offset, le64_to_cpu(s.v->inode) },
|
||||||
|
&inode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return inode.bi_dir != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_subvol_path(struct btree_trans *trans, struct btree_iter *iter, struct bkey_s_c k)
|
||||||
|
{
|
||||||
|
struct bch_fs *c = trans->c;
|
||||||
|
struct btree_iter parent_iter = {};
|
||||||
|
darray_u32 subvol_path = {};
|
||||||
|
struct printbuf buf = PRINTBUF;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (k.k->type != KEY_TYPE_subvolume)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (k.k->p.offset != BCACHEFS_ROOT_SUBVOL) {
|
||||||
|
ret = darray_push(&subvol_path, k.k->p.offset);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
struct bkey_s_c_subvolume s = bkey_s_c_to_subvolume(k);
|
||||||
|
|
||||||
|
ret = subvol_has_dirent(trans, s);
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (fsck_err_on(!ret,
|
||||||
|
c, subvol_unreachable,
|
||||||
|
"unreachable subvolume %s",
|
||||||
|
(bch2_bkey_val_to_text(&buf, c, s.s_c),
|
||||||
|
buf.buf))) {
|
||||||
|
ret = reattach_subvol(trans, s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 parent = le32_to_cpu(s.v->fs_path_parent);
|
||||||
|
|
||||||
|
if (darray_u32_has(&subvol_path, parent)) {
|
||||||
|
if (fsck_err(c, subvol_loop, "subvolume loop"))
|
||||||
|
ret = reattach_subvol(trans, s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bch2_trans_iter_exit(trans, &parent_iter);
|
||||||
|
bch2_trans_iter_init(trans, &parent_iter,
|
||||||
|
BTREE_ID_subvolumes, POS(0, parent), 0);
|
||||||
|
k = bch2_btree_iter_peek_slot(&parent_iter);
|
||||||
|
ret = bkey_err(k);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (fsck_err_on(k.k->type != KEY_TYPE_subvolume,
|
||||||
|
c, subvol_unreachable,
|
||||||
|
"unreachable subvolume %s",
|
||||||
|
(bch2_bkey_val_to_text(&buf, c, s.s_c),
|
||||||
|
buf.buf))) {
|
||||||
|
ret = reattach_subvol(trans, s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fsck_err:
|
||||||
|
err:
|
||||||
|
printbuf_exit(&buf);
|
||||||
|
darray_exit(&subvol_path);
|
||||||
|
bch2_trans_iter_exit(trans, &parent_iter);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bch2_check_subvolume_structure(struct bch_fs *c)
|
||||||
|
{
|
||||||
|
int ret = bch2_trans_run(c,
|
||||||
|
for_each_btree_key_commit(trans, iter,
|
||||||
|
BTREE_ID_subvolumes, POS_MIN, BTREE_ITER_PREFETCH, k,
|
||||||
|
NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
||||||
|
check_subvol_path(trans, &iter, k)));
|
||||||
|
bch_err_fn(c, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct pathbuf_entry {
|
struct pathbuf_entry {
|
||||||
u64 inum;
|
u64 inum;
|
||||||
u32 snapshot;
|
u32 snapshot;
|
||||||
@ -2124,20 +2246,6 @@ static bool path_is_dup(pathbuf *p, u64 inum, u32 snapshot)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int path_down(struct bch_fs *c, pathbuf *p,
|
|
||||||
u64 inum, u32 snapshot)
|
|
||||||
{
|
|
||||||
int ret = darray_push(p, ((struct pathbuf_entry) {
|
|
||||||
.inum = inum,
|
|
||||||
.snapshot = snapshot,
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
bch_err(c, "fsck: error allocating memory for pathbuf, size %zu",
|
|
||||||
p->size);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that a given inode is reachable from the root:
|
* Check that a given inode is reachable from the root:
|
||||||
*
|
*
|
||||||
@ -2188,11 +2296,12 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
|
|||||||
if (!S_ISDIR(inode.bi_mode))
|
if (!S_ISDIR(inode.bi_mode))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = path_down(c, p, inode.bi_inum, snapshot);
|
ret = darray_push(p, ((struct pathbuf_entry) {
|
||||||
if (ret) {
|
.inum = inode.bi_inum,
|
||||||
bch_err(c, "memory allocation failure");
|
.snapshot = snapshot,
|
||||||
|
}));
|
||||||
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
snapshot = parent_snapshot;
|
snapshot = parent_snapshot;
|
||||||
|
|
||||||
@ -2219,18 +2328,15 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
|
|||||||
pr_err("%llu:%u", i->inum, i->snapshot);
|
pr_err("%llu:%u", i->inum, i->snapshot);
|
||||||
pr_err("%llu:%u", inode.bi_inum, snapshot);
|
pr_err("%llu:%u", inode.bi_inum, snapshot);
|
||||||
|
|
||||||
if (!fsck_err(c, dir_loop, "directory structure loop"))
|
if (fsck_err(c, dir_loop, "directory structure loop")) {
|
||||||
return 0;
|
ret = remove_backpointer(trans, &inode);
|
||||||
|
|
||||||
ret = remove_backpointer(trans, &inode);
|
|
||||||
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
|
||||||
bch_err_msg(c, ret, "removing dirent");
|
bch_err_msg(c, ret, "removing dirent");
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = reattach_inode(trans, &inode, snapshot);
|
ret = reattach_inode(trans, &inode, snapshot);
|
||||||
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
|
||||||
bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
|
bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ int bch2_check_indirect_extents(struct bch_fs *);
|
|||||||
int bch2_check_dirents(struct bch_fs *);
|
int bch2_check_dirents(struct bch_fs *);
|
||||||
int bch2_check_xattrs(struct bch_fs *);
|
int bch2_check_xattrs(struct bch_fs *);
|
||||||
int bch2_check_root(struct bch_fs *);
|
int bch2_check_root(struct bch_fs *);
|
||||||
|
int bch2_check_subvolume_structure(struct bch_fs *);
|
||||||
int bch2_check_directory_structure(struct bch_fs *);
|
int bch2_check_directory_structure(struct bch_fs *);
|
||||||
int bch2_check_nlinks(struct bch_fs *);
|
int bch2_check_nlinks(struct bch_fs *);
|
||||||
int bch2_fix_reflink_p(struct bch_fs *);
|
int bch2_fix_reflink_p(struct bch_fs *);
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
x(check_dirents, 27, PASS_FSCK) \
|
x(check_dirents, 27, PASS_FSCK) \
|
||||||
x(check_xattrs, 28, PASS_FSCK) \
|
x(check_xattrs, 28, PASS_FSCK) \
|
||||||
x(check_root, 29, PASS_ONLINE|PASS_FSCK) \
|
x(check_root, 29, PASS_ONLINE|PASS_FSCK) \
|
||||||
|
x(check_subvolume_structure, 36, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(check_directory_structure, 30, PASS_ONLINE|PASS_FSCK) \
|
x(check_directory_structure, 30, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(check_nlinks, 31, PASS_FSCK) \
|
x(check_nlinks, 31, PASS_FSCK) \
|
||||||
x(delete_dead_inodes, 32, PASS_FSCK|PASS_UNCLEAN) \
|
x(delete_dead_inodes, 32, PASS_FSCK|PASS_UNCLEAN) \
|
||||||
|
Loading…
Reference in New Issue
Block a user