Skip to content

Commit

Permalink
bcachefs: Fix fsck directory i_size checking
Browse files Browse the repository at this point in the history
Error handling was wrong, causing unhandled transaction restart errors.

check_directory_size() was also inefficient, since keys in multiple
snapshots would be iterated over once for every snapshot. Convert it to
the same scheme used for i_sectors and subdir count checking.

Cc: Hongbo Li <lihongbo22@huawei.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
  • Loading branch information
Kent Overstreet committed Feb 19, 2025
1 parent 406e445 commit b9ddb3e
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 48 deletions.
78 changes: 31 additions & 47 deletions fs/bcachefs/fsck.c
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ struct inode_walker_entry {
struct bch_inode_unpacked inode;
u32 snapshot;
u64 count;
u64 i_size;
};

struct inode_walker {
Expand Down Expand Up @@ -910,8 +911,9 @@ lookup_inode_for_snapshot(struct bch_fs *c, struct inode_walker *w, struct bkey_
if (k.k->p.snapshot != i->snapshot && !is_whiteout) {
struct inode_walker_entry new = *i;

new.snapshot = k.k->p.snapshot;
new.count = 0;
new.snapshot = k.k->p.snapshot;
new.count = 0;
new.i_size = 0;

struct printbuf buf = PRINTBUF;
bch2_bkey_val_to_text(&buf, c, k);
Expand Down Expand Up @@ -1116,37 +1118,6 @@ static int get_snapshot_root_inode(struct btree_trans *trans,
return ret;
}

static int check_directory_size(struct btree_trans *trans,
struct bch_inode_unpacked *inode_u,
struct bkey_s_c inode_k, bool *write_inode)
{
struct btree_iter iter;
struct bkey_s_c k;
u64 new_size = 0;
int ret;

for_each_btree_key_max_norestart(trans, iter, BTREE_ID_dirents,
SPOS(inode_k.k->p.offset, 0, inode_k.k->p.snapshot),
POS(inode_k.k->p.offset, U64_MAX),
0, k, ret) {
if (k.k->type != KEY_TYPE_dirent)
continue;

struct bkey_s_c_dirent dirent = bkey_s_c_to_dirent(k);
struct qstr name = bch2_dirent_get_name(dirent);

new_size += dirent_occupied_size(&name);
}
bch2_trans_iter_exit(trans, &iter);

if (!ret && inode_u->bi_size != new_size) {
inode_u->bi_size = new_size;
*write_inode = true;
}

return ret;
}

static int check_inode(struct btree_trans *trans,
struct btree_iter *iter,
struct bkey_s_c k,
Expand Down Expand Up @@ -1335,16 +1306,6 @@ static int check_inode(struct btree_trans *trans,
u.bi_journal_seq = journal_cur_seq(&c->journal);
do_update = true;
}

if (S_ISDIR(u.bi_mode)) {
ret = check_directory_size(trans, &u, k, &do_update);

fsck_err_on(ret,
trans, directory_size_mismatch,
"directory inode %llu:%u with the mismatch directory size",
u.bi_inum, k.k->p.snapshot);
ret = 0;
}
do_update:
if (do_update) {
ret = __bch2_fsck_write_inode(trans, &u);
Expand Down Expand Up @@ -2017,10 +1978,31 @@ static int check_subdir_count_notnested(struct btree_trans *trans, struct inode_
return ret;
}

static int check_subdir_count(struct btree_trans *trans, struct inode_walker *w)
static int check_dir_i_size_notnested(struct btree_trans *trans, struct inode_walker *w)
{
struct bch_fs *c = trans->c;
int ret = 0;

darray_for_each(w->inodes, i)
if (fsck_err_on(i->inode.bi_size != i->i_size,
trans, inode_dir_wrong_nlink,
"directory %llu:%u with wrong i_size: got %llu, should be %llu",
w->last_pos.inode, i->snapshot, i->inode.bi_size, i->i_size)) {
i->inode.bi_size = i->i_size;
ret = bch2_fsck_write_inode(trans, &i->inode);
if (ret)
break;
}
fsck_err:
bch_err_fn(c, ret);
return ret;
}

static int check_subdir_dirents_count(struct btree_trans *trans, struct inode_walker *w)
{
u32 restart_count = trans->restart_count;
return check_subdir_count_notnested(trans, w) ?:
check_dir_i_size_notnested(trans, w) ?:
trans_was_restarted(trans, restart_count);
}

Expand Down Expand Up @@ -2367,7 +2349,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
goto out;

if (dir->last_pos.inode != k.k->p.inode && dir->have_inodes) {
ret = check_subdir_count(trans, dir);
ret = check_subdir_dirents_count(trans, dir);
if (ret)
goto err;
}
Expand Down Expand Up @@ -2457,9 +2439,11 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
if (ret)
goto err;

if (d.v->d_type == DT_DIR)
for_each_visible_inode(c, s, dir, d.k->p.snapshot, i)
for_each_visible_inode(c, s, dir, d.k->p.snapshot, i) {
if (d.v->d_type == DT_DIR)
i->count++;
i->i_size += bkey_bytes(d.k);
}
out:
err:
fsck_err:
Expand Down
2 changes: 1 addition & 1 deletion fs/bcachefs/sb-downgrade.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
BCH_FSCK_ERR_accounting_key_replicas_nr_devs_0, \
BCH_FSCK_ERR_accounting_key_junk_at_end) \
x(directory_size, \
BIT_ULL(BCH_RECOVERY_PASS_check_inodes), \
BIT_ULL(BCH_RECOVERY_PASS_check_dirents), \
BCH_FSCK_ERR_directory_size_mismatch) \

#define DOWNGRADE_TABLE() \
Expand Down

0 comments on commit b9ddb3e

Please sign in to comment.