Commit b906aadd authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: Redo check_nlink fsck pass

Now that we have inode backpointers the check_nlink pass only is
concerned with files that have hardlinks, and can be simplified.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 8a85b20c
......@@ -1137,14 +1137,12 @@ static int check_directory_structure(struct bch_fs *c,
struct nlink {
u32 count;
u32 dir_count;
};
typedef GENRADIX(struct nlink) nlink_table;
static void inc_link(struct bch_fs *c, nlink_table *links,
u64 range_start, u64 *range_end,
u64 inum, bool dir)
u64 range_start, u64 *range_end, u64 inum)
{
struct nlink *link;
......@@ -1163,10 +1161,7 @@ static void inc_link(struct bch_fs *c, nlink_table *links,
return;
}
if (dir)
link->dir_count++;
else
link->count++;
link->count++;
}
noinline_for_stack
......@@ -1177,26 +1172,18 @@ static int bch2_gc_walk_dirents(struct bch_fs *c, nlink_table *links,
struct btree_iter *iter;
struct bkey_s_c k;
struct bkey_s_c_dirent d;
u64 d_inum;
int ret;
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
inc_link(c, links, range_start, range_end, BCACHEFS_ROOT_INO, false);
for_each_btree_key(&trans, iter, BTREE_ID_dirents, POS_MIN, 0, k, ret) {
switch (k.k->type) {
case KEY_TYPE_dirent:
d = bkey_s_c_to_dirent(k);
d_inum = le64_to_cpu(d.v->d_inum);
if (d.v->d_type == DT_DIR)
if (d.v->d_type != DT_DIR)
inc_link(c, links, range_start, range_end,
d.k->p.inode, true);
inc_link(c, links, range_start, range_end,
d_inum, false);
le64_to_cpu(d.v->d_inum));
break;
}
......@@ -1215,99 +1202,48 @@ static int check_inode_nlink(struct btree_trans *trans,
struct bch_inode_unpacked *lostfound_inode,
struct btree_iter *iter,
struct bkey_s_c_inode inode,
struct nlink *link)
unsigned nlink)
{
struct bch_fs *c = trans->c;
struct bch_inode_unpacked u;
u32 i_nlink, real_i_nlink;
int ret = 0;
/*
* Backpointer and directory structure checks are sufficient for
* directories, since they can't have hardlinks:
*/
if (S_ISDIR(le16_to_cpu(inode.v->bi_mode)))
return 0;
ret = bch2_inode_unpack(inode, &u);
/* Should never happen, checked by bch2_inode_invalid: */
if (bch2_fs_inconsistent_on(ret, c,
"error unpacking inode %llu in fsck",
inode.k->p.inode))
return ret;
i_nlink = bch2_inode_nlink_get(&u);
real_i_nlink = link->count * nlink_bias(u.bi_mode) + link->dir_count;
/*
* These should have been caught/fixed by earlier passes, we don't
* repair them here:
*/
if (S_ISDIR(u.bi_mode) && link->count > 1) {
need_fsck_err(c, "directory %llu with multiple hardlinks: %u",
u.bi_inum, link->count);
return 0;
}
if (S_ISDIR(u.bi_mode) && !link->count) {
need_fsck_err(c, "unreachable directory found (inum %llu)",
u.bi_inum);
return 0;
}
if (!S_ISDIR(u.bi_mode) && link->dir_count) {
need_fsck_err(c, "non directory with subdirectories (inum %llu)",
u.bi_inum);
return 0;
}
if (!link->count &&
!(u.bi_flags & BCH_INODE_UNLINKED) &&
(c->sb.features & (1 << BCH_FEATURE_atomic_nlink))) {
if (fsck_err(c, "unreachable inode %llu not marked as unlinked (type %u)",
u.bi_inum, mode_to_type(u.bi_mode)) ==
FSCK_ERR_IGNORE)
return 0;
/* Improved directory structure pass will catch this: */
if (fsck_err_on(!nlink, c,
"unreachable inode %llu not marked as unlinked (type %u)",
u.bi_inum, mode_to_type(u.bi_mode))) {
ret = reattach_inode(c, lostfound_inode, u.bi_inum);
if (ret)
return ret;
link->count = 1;
real_i_nlink = nlink_bias(u.bi_mode) + link->dir_count;
goto set_i_nlink;
}
if (i_nlink < link->count) {
if (fsck_err(c, "inode %llu i_link too small (%u < %u, type %i)",
u.bi_inum, i_nlink, link->count,
mode_to_type(u.bi_mode)) == FSCK_ERR_IGNORE)
return 0;
goto set_i_nlink;
}
if (i_nlink != real_i_nlink &&
c->sb.clean) {
if (fsck_err(c, "filesystem marked clean, "
"but inode %llu has wrong i_nlink "
"(type %u i_nlink %u, should be %u)",
u.bi_inum, mode_to_type(u.bi_mode),
i_nlink, real_i_nlink) == FSCK_ERR_IGNORE)
return 0;
goto set_i_nlink;
}
if (i_nlink != real_i_nlink &&
(c->sb.features & (1 << BCH_FEATURE_atomic_nlink))) {
if (fsck_err(c, "inode %llu has wrong i_nlink "
"(type %u i_nlink %u, should be %u)",
u.bi_inum, mode_to_type(u.bi_mode),
i_nlink, real_i_nlink) == FSCK_ERR_IGNORE)
return 0;
goto set_i_nlink;
nlink = 1;
}
if (real_i_nlink && i_nlink != real_i_nlink)
bch_verbose(c, "setting inode %llu nlink from %u to %u",
u.bi_inum, i_nlink, real_i_nlink);
set_i_nlink:
if (i_nlink != real_i_nlink) {
if (fsck_err_on(bch2_inode_nlink_get(&u) != nlink, c,
"inode %llu has wrong i_nlink (type %u i_nlink %u, should be %u)",
u.bi_inum, mode_to_type(u.bi_mode),
bch2_inode_nlink_get(&u), nlink)) {
struct bkey_inode_buf p;
bch2_inode_nlink_set(&u, real_i_nlink);
if (nlink > 1)
u.bi_flags |= BCH_INODE_BACKPTR_UNTRUSTED;
bch2_inode_nlink_set(&u, nlink);
bch2_inode_pack(c, &p, &u);
p.inode.k.p = iter->pos;
......@@ -1331,66 +1267,33 @@ static int bch2_gc_walk_inodes(struct bch_fs *c,
struct btree_trans trans;
struct btree_iter *iter;
struct bkey_s_c k;
struct nlink *link, zero_links = { 0, 0 };
struct genradix_iter nlinks_iter;
int ret = 0, ret2 = 0;
u64 nlinks_pos;
struct nlink *link;
int ret = 0;
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_inodes,
POS(0, range_start), 0);
nlinks_iter = genradix_iter_init(links, 0);
while ((k = bch2_btree_iter_peek(iter)).k &&
!(ret2 = bkey_err(k)) &&
iter->pos.offset < range_end) {
peek_nlinks: link = genradix_iter_peek(&nlinks_iter, links);
if (!link && (!k.k || iter->pos.offset >= range_end))
for_each_btree_key(&trans, iter, BTREE_ID_inodes,
POS(0, range_start), 0, k, ret) {
if (!k.k || k.k->p.offset >= range_end)
break;
nlinks_pos = range_start + nlinks_iter.pos;
if (link && nlinks_pos < iter->pos.offset) {
/* Should have been caught by dirents pass: */
need_fsck_err_on(link->count, c,
"missing inode %llu (nlink %u)",
nlinks_pos, link->count);
genradix_iter_advance(&nlinks_iter, links);
goto peek_nlinks;
}
if (!link || nlinks_pos > iter->pos.offset)
link = &zero_links;
if (k.k && k.k->type == KEY_TYPE_inode) {
ret = check_inode_nlink(&trans, lostfound_inode, iter,
bkey_s_c_to_inode(k), link);
BUG_ON(ret == -EINTR);
if (ret)
break;
} else {
/* Should have been caught by dirents pass: */
need_fsck_err_on(link->count, c,
"missing inode %llu (nlink %u)",
nlinks_pos, link->count);
}
if (k.k->type != KEY_TYPE_inode)
continue;
if (nlinks_pos == iter->pos.offset)
genradix_iter_advance(&nlinks_iter, links);
link = genradix_ptr(links, k.k->p.offset - range_start);
ret = check_inode_nlink(&trans, lostfound_inode, iter,
bkey_s_c_to_inode(k), link ? link->count : 0);
if (ret)
break;
bch2_btree_iter_advance(iter);
bch2_trans_cond_resched(&trans);
}
fsck_err:
bch2_trans_iter_put(&trans, iter);
bch2_trans_exit(&trans);
if (ret2)
bch_err(c, "error in fsck: btree error %i while walking inodes", ret2);
if (ret)
bch_err(c, "error in fsck: btree error %i while walking inodes", ret);
return ret ?: ret2;
return ret;
}
noinline_for_stack
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment