diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2021-04-08 22:25:29 +0300 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-23 00:09:00 +0300 |
commit | b906aaddf2144b9f4ebdb8618e8ab1af00a58644 (patch) | |
tree | c60b7919eb214cc61848a621dca1db3f2bf33c40 | |
parent | 8a85b20cd757d9ebc784adc7d56ea378b9bf30c5 (diff) | |
download | linux-b906aaddf2144b9f4ebdb8618e8ab1af00a58644.tar.xz |
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: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | fs/bcachefs/fsck.c | 179 |
1 files changed, 41 insertions, 138 deletions
diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 5be86bf60545..6bc3f2f09e36 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -1137,14 +1137,12 @@ fsck_err: 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 |