Commit 914f2786 authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: Improvements to fsck check_dirents()

The fsck code handles transaction restarts in a very ad hoc way, and not
always correctly. This patch makes some improvements to check_dirents(),
but more work needs to be done to figure out how this kind of code
should be structured.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@gmail.com>
parent 5aab6635
...@@ -267,11 +267,11 @@ static struct inode_walker inode_walker_init(void) ...@@ -267,11 +267,11 @@ static struct inode_walker inode_walker_init(void)
}; };
} }
static int walk_inode(struct btree_trans *trans, static int __walk_inode(struct btree_trans *trans,
struct inode_walker *w, u64 inum) struct inode_walker *w, u64 inum)
{ {
if (inum != w->cur_inum) { if (inum != w->cur_inum) {
int ret = lookup_inode(trans, inum, &w->inode, &w->snapshot); int ret = __lookup_inode(trans, inum, &w->inode, &w->snapshot);
if (ret && ret != -ENOENT) if (ret && ret != -ENOENT)
return ret; return ret;
...@@ -286,6 +286,12 @@ static int walk_inode(struct btree_trans *trans, ...@@ -286,6 +286,12 @@ static int walk_inode(struct btree_trans *trans,
return 0; return 0;
} }
static int walk_inode(struct btree_trans *trans,
struct inode_walker *w, u64 inum)
{
return lockrestart_do(trans, __walk_inode(trans, w, inum));
}
static int hash_redo_key(struct btree_trans *trans, static int hash_redo_key(struct btree_trans *trans,
const struct bch_hash_desc desc, const struct bch_hash_desc desc,
struct bch_hash_info *hash_info, struct bch_hash_info *hash_info,
...@@ -704,98 +710,78 @@ static int check_extents(struct bch_fs *c) ...@@ -704,98 +710,78 @@ static int check_extents(struct bch_fs *c)
return bch2_trans_exit(&trans) ?: ret; return bch2_trans_exit(&trans) ?: ret;
} }
/* static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
* Walk dirents: verify that they all have a corresponding S_ISDIR inode, struct bch_hash_info *hash_info,
* validate d_type struct inode_walker *w, unsigned *nr_subdirs)
*/
noinline_for_stack
static int check_dirents(struct bch_fs *c)
{ {
struct inode_walker w = inode_walker_init(); struct bch_fs *c = trans->c;
struct bch_hash_info hash_info;
struct btree_trans trans;
struct btree_iter *iter;
struct bkey_s_c k; struct bkey_s_c k;
char buf[200];
unsigned nr_subdirs = 0;
int ret = 0;
bch_verbose(c, "checking dirents");
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_dirents,
POS(BCACHEFS_ROOT_INO, 0),
BTREE_ITER_INTENT|
BTREE_ITER_PREFETCH);
retry:
while ((k = bch2_btree_iter_peek(iter)).k &&
!(ret = bkey_err(k))) {
struct bkey_s_c_dirent d; struct bkey_s_c_dirent d;
struct bch_inode_unpacked target; struct bch_inode_unpacked target;
u32 target_snapshot; u32 target_snapshot;
bool have_target; bool have_target;
bool backpointer_exists = true; bool backpointer_exists = true;
u64 d_inum; u64 d_inum;
char buf[200];
int ret;
if (w.have_inode && k = bch2_btree_iter_peek(iter);
w.cur_inum != k.k->p.inode && if (!k.k)
fsck_err_on(w.inode.bi_nlink != nr_subdirs, c, return 1;
"directory %llu with wrong i_nlink: got %u, should be %u",
w.inode.bi_inum, w.inode.bi_nlink, nr_subdirs)) { ret = bkey_err(k);
w.inode.bi_nlink = nr_subdirs;
ret = write_inode(&trans, &w.inode, w.snapshot);
if (ret) if (ret)
break; return ret;
if (w->have_inode &&
w->cur_inum != k.k->p.inode &&
fsck_err_on(w->inode.bi_nlink != *nr_subdirs, c,
"directory %llu with wrong i_nlink: got %u, should be %u",
w->inode.bi_inum, w->inode.bi_nlink, *nr_subdirs)) {
w->inode.bi_nlink = *nr_subdirs;
ret = write_inode(trans, &w->inode, w->snapshot);
return ret ?: -EINTR;
} }
ret = walk_inode(&trans, &w, k.k->p.inode); ret = __walk_inode(trans, w, k.k->p.inode);
if (ret) if (ret)
break; return ret;
if (w.first_this_inode) if (w->first_this_inode)
nr_subdirs = 0; *nr_subdirs = 0;
if (fsck_err_on(!w.have_inode, c, if (fsck_err_on(!w->have_inode, c,
"dirent in nonexisting directory:\n%s", "dirent in nonexisting directory:\n%s",
(bch2_bkey_val_to_text(&PBUF(buf), c, (bch2_bkey_val_to_text(&PBUF(buf), c, k), buf)) ||
k), buf)) || fsck_err_on(!S_ISDIR(w->inode.bi_mode), c,
fsck_err_on(!S_ISDIR(w.inode.bi_mode), c,
"dirent in non directory inode type %u:\n%s", "dirent in non directory inode type %u:\n%s",
mode_to_type(w.inode.bi_mode), mode_to_type(w->inode.bi_mode),
(bch2_bkey_val_to_text(&PBUF(buf), c, (bch2_bkey_val_to_text(&PBUF(buf), c, k), buf)))
k), buf))) { return __bch2_trans_do(trans, NULL, NULL, 0,
ret = __bch2_trans_do(&trans, NULL, NULL, 0, bch2_btree_delete_at(trans, iter, 0));
bch2_btree_delete_at(&trans, iter, 0));
if (ret)
goto err;
goto next;
}
if (!w.have_inode) if (!w->have_inode)
goto next; return 0;
if (w.first_this_inode) if (w->first_this_inode)
hash_info = bch2_hash_info_init(c, &w.inode); *hash_info = bch2_hash_info_init(c, &w->inode);
ret = hash_check_key(&trans, bch2_dirent_hash_desc, ret = hash_check_key(trans, bch2_dirent_hash_desc,
&hash_info, iter, k); hash_info, iter, k);
if (ret > 0) { if (ret < 0)
ret = 0; return ret;
goto next; if (ret) /* dirent has been deleted */
} return 0;
if (ret)
goto fsck_err;
if (k.k->type != KEY_TYPE_dirent) if (k.k->type != KEY_TYPE_dirent)
goto next; return 0;
d = bkey_s_c_to_dirent(k); d = bkey_s_c_to_dirent(k);
d_inum = le64_to_cpu(d.v->d_inum); d_inum = le64_to_cpu(d.v->d_inum);
ret = lookup_inode(&trans, d_inum, &target, &target_snapshot); ret = __lookup_inode(trans, d_inum, &target, &target_snapshot);
if (ret && ret != -ENOENT) if (ret && ret != -ENOENT)
break; return ret;
have_target = !ret; have_target = !ret;
ret = 0; ret = 0;
...@@ -803,30 +789,31 @@ static int check_dirents(struct bch_fs *c) ...@@ -803,30 +789,31 @@ static int check_dirents(struct bch_fs *c)
if (fsck_err_on(!have_target, c, if (fsck_err_on(!have_target, c,
"dirent points to missing inode:\n%s", "dirent points to missing inode:\n%s",
(bch2_bkey_val_to_text(&PBUF(buf), c, (bch2_bkey_val_to_text(&PBUF(buf), c,
k), buf))) { k), buf)))
ret = remove_dirent(&trans, d.k->p); return remove_dirent(trans, d.k->p);
if (ret)
goto err;
goto next;
}
if (!have_target) if (!have_target)
goto next; return 0;
if (!target.bi_dir && if (!target.bi_dir &&
!target.bi_dir_offset) { !target.bi_dir_offset) {
target.bi_dir = k.k->p.inode; target.bi_dir = k.k->p.inode;
target.bi_dir_offset = k.k->p.offset; target.bi_dir_offset = k.k->p.offset;
ret = write_inode(&trans, &target, target_snapshot); ret = __write_inode(trans, &target, target_snapshot) ?:
bch2_trans_commit(trans, NULL, NULL,
BTREE_INSERT_NOFAIL|
BTREE_INSERT_LAZY_RW|
BTREE_INSERT_NOUNLOCK);
if (ret) if (ret)
goto err; return ret;
return -EINTR;
} }
if (!inode_backpointer_matches(d, &target)) { if (!inode_backpointer_matches(d, &target)) {
ret = inode_backpointer_exists(&trans, &target); ret = inode_backpointer_exists(trans, &target);
if (ret < 0) if (ret < 0)
goto err; return ret;
backpointer_exists = ret; backpointer_exists = ret;
ret = 0; ret = 0;
...@@ -834,12 +821,8 @@ static int check_dirents(struct bch_fs *c) ...@@ -834,12 +821,8 @@ static int check_dirents(struct bch_fs *c)
if (fsck_err_on(S_ISDIR(target.bi_mode) && if (fsck_err_on(S_ISDIR(target.bi_mode) &&
backpointer_exists, c, backpointer_exists, c,
"directory %llu with multiple links", "directory %llu with multiple links",
target.bi_inum)) { target.bi_inum))
ret = remove_dirent(&trans, d.k->p); return remove_dirent(trans, d.k->p);
if (ret)
goto err;
continue;
}
if (fsck_err_on(backpointer_exists && if (fsck_err_on(backpointer_exists &&
!target.bi_nlink, c, !target.bi_nlink, c,
...@@ -848,9 +831,8 @@ static int check_dirents(struct bch_fs *c) ...@@ -848,9 +831,8 @@ static int check_dirents(struct bch_fs *c)
target.bi_nlink++; target.bi_nlink++;
target.bi_flags &= ~BCH_INODE_UNLINKED; target.bi_flags &= ~BCH_INODE_UNLINKED;
ret = write_inode(&trans, &target, target_snapshot); ret = write_inode(trans, &target, target_snapshot);
if (ret) return ret ?: -EINTR;
goto err;
} }
if (fsck_err_on(!backpointer_exists, c, if (fsck_err_on(!backpointer_exists, c,
...@@ -865,9 +847,8 @@ static int check_dirents(struct bch_fs *c) ...@@ -865,9 +847,8 @@ static int check_dirents(struct bch_fs *c)
target.bi_dir = k.k->p.inode; target.bi_dir = k.k->p.inode;
target.bi_dir_offset = k.k->p.offset; target.bi_dir_offset = k.k->p.offset;
ret = write_inode(&trans, &target, target_snapshot); ret = write_inode(trans, &target, target_snapshot);
if (ret) return ret ?: -EINTR;
goto err;
} }
} }
...@@ -879,35 +860,65 @@ static int check_dirents(struct bch_fs *c) ...@@ -879,35 +860,65 @@ static int check_dirents(struct bch_fs *c)
struct bkey_i_dirent *n; struct bkey_i_dirent *n;
n = kmalloc(bkey_bytes(d.k), GFP_KERNEL); n = kmalloc(bkey_bytes(d.k), GFP_KERNEL);
if (!n) { if (!n)
ret = -ENOMEM; return -ENOMEM;
goto err;
}
bkey_reassemble(&n->k_i, d.s_c); bkey_reassemble(&n->k_i, d.s_c);
n->v.d_type = mode_to_type(target.bi_mode); n->v.d_type = mode_to_type(target.bi_mode);
ret = __bch2_trans_do(&trans, NULL, NULL, ret = __bch2_trans_do(trans, NULL, NULL,
BTREE_INSERT_NOFAIL| BTREE_INSERT_NOFAIL|
BTREE_INSERT_LAZY_RW, BTREE_INSERT_LAZY_RW,
bch2_btree_iter_traverse(iter) ?: bch2_btree_iter_traverse(iter) ?:
bch2_trans_update(&trans, iter, &n->k_i, 0)); bch2_trans_update(trans, iter, &n->k_i, 0));
kfree(n); kfree(n);
if (ret) return ret ?: -EINTR;
goto err; }
*nr_subdirs += d.v->d_type == DT_DIR;
return 0;
fsck_err:
return ret;
}
/*
* Walk dirents: verify that they all have a corresponding S_ISDIR inode,
* validate d_type
*/
noinline_for_stack
static int check_dirents(struct bch_fs *c)
{
struct inode_walker w = inode_walker_init();
struct bch_hash_info hash_info;
struct btree_trans trans;
struct btree_iter *iter;
unsigned nr_subdirs = 0;
int ret = 0;
bch_verbose(c, "checking dirents");
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_dirents,
POS(BCACHEFS_ROOT_INO, 0),
BTREE_ITER_INTENT|
BTREE_ITER_PREFETCH);
while (1) {
ret = lockrestart_do(&trans,
check_dirent(&trans, iter, &hash_info, &w, &nr_subdirs));
if (ret == 1) {
/* at end */
ret = 0;
break;
} }
if (ret)
break;
nr_subdirs += d.v->d_type == DT_DIR;
next:
bch2_btree_iter_advance(iter); bch2_btree_iter_advance(iter);
} }
err:
fsck_err:
if (ret == -EINTR)
goto retry;
bch2_trans_iter_put(&trans, iter); bch2_trans_iter_put(&trans, iter);
return bch2_trans_exit(&trans) ?: ret; return bch2_trans_exit(&trans) ?: ret;
} }
......
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