#include "bcachefs.h"
#include "bkey_buf.h"
#include "btree_update.h"
+#include "buckets.h"
#include "darray.h"
#include "dirent.h"
#include "error.h"
static int __snapshot_lookup_subvol(struct btree_trans *trans, u32 snapshot,
u32 *subvol)
{
- struct btree_iter iter;
- struct bkey_s_c k;
- int ret;
-
- bch2_trans_iter_init(trans, &iter, BTREE_ID_snapshots,
- POS(0, snapshot), 0);
- k = bch2_btree_iter_peek_slot(&iter);
- ret = bkey_err(k);
- if (ret)
- goto err;
-
- if (k.k->type != KEY_TYPE_snapshot) {
+ struct bch_snapshot s;
+ int ret = bch2_bkey_get_val_typed(trans, BTREE_ID_snapshots,
+ POS(0, snapshot), 0,
+ snapshot, &s);
+ if (!ret)
+ *subvol = le32_to_cpu(s.subvol);
+ else if (bch2_err_matches(ret, ENOENT))
bch_err(trans->c, "snapshot %u not fonud", snapshot);
- ret = -ENOENT;
- goto err;
- }
-
- *subvol = le32_to_cpu(bkey_s_c_to_snapshot(k).v->subvol);
-err:
- bch2_trans_iter_exit(trans, &iter);
return ret;
}
goto err;
if (!k.k || !bkey_eq(k.k->p, POS(0, inode_nr))) {
- ret = -ENOENT;
+ ret = -BCH_ERR_ENOENT_inode;
goto err;
}
struct bkey_s_c k;
int ret;
- bch2_trans_iter_init(trans, &iter, BTREE_ID_inodes,
- SPOS(0, inode_nr, *snapshot), 0);
- k = bch2_btree_iter_peek_slot(&iter);
+ k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
+ SPOS(0, inode_nr, *snapshot), 0);
ret = bkey_err(k);
if (ret)
goto err;
ret = bkey_is_inode(k.k)
? bch2_inode_unpack(k, inode)
- : -ENOENT;
+ : -BCH_ERR_ENOENT_inode;
if (!ret)
*snapshot = iter.pos.snapshot;
err:
struct bch_inode_unpacked *inode,
u32 snapshot)
{
- struct btree_iter iter;
- int ret;
+ struct bkey_inode_buf *inode_p =
+ bch2_trans_kmalloc(trans, sizeof(*inode_p));
- bch2_trans_iter_init(trans, &iter, BTREE_ID_inodes,
- SPOS(0, inode->bi_inum, snapshot),
- BTREE_ITER_INTENT);
+ if (IS_ERR(inode_p))
+ return PTR_ERR(inode_p);
- ret = bch2_btree_iter_traverse(&iter) ?:
- bch2_inode_write(trans, &iter, inode);
- bch2_trans_iter_exit(trans, &iter);
- return ret;
+ bch2_inode_pack(inode_p, inode);
+ inode_p->inode.k.p.snapshot = snapshot;
+
+ return bch2_btree_insert_nonextent(trans, BTREE_ID_inodes,
+ &inode_p->inode.k_i,
+ BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
}
static int write_inode(struct btree_trans *trans,
retry:
bch2_trans_begin(trans);
- bch2_trans_iter_init(trans, &iter, BTREE_ID_inodes,
- SPOS(0, inum, snapshot), BTREE_ITER_INTENT);
- k = bch2_btree_iter_peek_slot(&iter);
-
+ k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
+ SPOS(0, inum, snapshot), BTREE_ITER_INTENT);
ret = bkey_err(k);
if (ret)
goto err;
bch2_trans_iter_exit(trans, &iter);
err:
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
ret = __lookup_dirent(trans, root_hash_info, root_inum,
&lostfound_str, &inum, &d_type);
- if (ret == -ENOENT) {
+ if (bch2_err_matches(ret, ENOENT)) {
bch_notice(c, "creating lost+found");
goto create_lostfound;
}
}
/*
- * The check_dirents pass has already run, dangling dirents
+ * The bch2_check_dirents pass has already run, dangling dirents
* shouldn't exist here:
*/
return __lookup_inode(trans, inum, lostfound, &snapshot);
struct bch_inode_unpacked *inode)
{
struct btree_iter iter;
- struct bkey_s_c k;
+ struct bkey_s_c_dirent d;
int ret;
- bch2_trans_iter_init(trans, &iter, BTREE_ID_dirents,
- POS(inode->bi_dir, inode->bi_dir_offset), 0);
- k = bch2_btree_iter_peek_slot(&iter);
- ret = bkey_err(k);
- if (ret)
- goto out;
- if (k.k->type != KEY_TYPE_dirent) {
- ret = -ENOENT;
- goto out;
- }
-
- ret = __remove_dirent(trans, k.k->p);
-out:
+ d = bch2_bkey_get_iter_typed(trans, &iter, BTREE_ID_dirents,
+ POS(inode->bi_dir, inode->bi_dir_offset), 0,
+ dirent);
+ ret = bkey_err(d) ?:
+ __remove_dirent(trans, d.k->p);
bch2_trans_iter_exit(trans, &iter);
return ret;
}
}));
}
-static int __walk_inode(struct btree_trans *trans,
- struct inode_walker *w, struct bpos pos)
+static int get_inodes_all_snapshots(struct btree_trans *trans,
+ struct inode_walker *w, u64 inum)
{
struct bch_fs *c = trans->c;
struct btree_iter iter;
struct bkey_s_c k;
u32 restart_count = trans->restart_count;
- unsigned i;
int ret;
- pos.snapshot = bch2_snapshot_equiv(c, pos.snapshot);
-
- if (pos.inode == w->cur_inum) {
- w->first_this_inode = false;
- goto lookup_snapshot;
- }
+ if (w->cur_inum == inum)
+ return 0;
w->inodes.nr = 0;
- for_each_btree_key(trans, iter, BTREE_ID_inodes, POS(0, pos.inode),
+ for_each_btree_key(trans, iter, BTREE_ID_inodes, POS(0, inum),
BTREE_ITER_ALL_SNAPSHOTS, k, ret) {
- if (k.k->p.offset != pos.inode)
+ if (k.k->p.offset != inum)
break;
if (bkey_is_inode(k.k))
if (ret)
return ret;
- w->cur_inum = pos.inode;
+ w->cur_inum = inum;
w->first_this_inode = true;
if (trans_was_restarted(trans, restart_count))
return -BCH_ERR_transaction_restart_nested;
-lookup_snapshot:
- for (i = 0; i < w->inodes.nr; i++)
- if (bch2_snapshot_is_ancestor(c, pos.snapshot, w->inodes.data[i].snapshot))
+ return 0;
+}
+
+static struct inode_walker_entry *
+lookup_inode_for_snapshot(struct bch_fs *c,
+ struct inode_walker *w, u32 snapshot)
+{
+ struct inode_walker_entry *i;
+
+ snapshot = bch2_snapshot_equiv(c, snapshot);
+
+ darray_for_each(w->inodes, i)
+ if (bch2_snapshot_is_ancestor(c, snapshot, i->snapshot))
goto found;
- return INT_MAX;
+
+ return NULL;
found:
- BUG_ON(pos.snapshot > w->inodes.data[i].snapshot);
+ BUG_ON(snapshot > i->snapshot);
- if (pos.snapshot != w->inodes.data[i].snapshot) {
- struct inode_walker_entry e = w->inodes.data[i];
+ if (snapshot != i->snapshot) {
+ struct inode_walker_entry new = *i;
+ int ret;
- e.snapshot = pos.snapshot;
- e.count = 0;
+ new.snapshot = snapshot;
+ new.count = 0;
bch_info(c, "have key for inode %llu:%u but have inode in ancestor snapshot %u",
- pos.inode, pos.snapshot, w->inodes.data[i].snapshot);
+ w->cur_inum, snapshot, i->snapshot);
- while (i && w->inodes.data[i - 1].snapshot > pos.snapshot)
+ while (i > w->inodes.data && i[-1].snapshot > snapshot)
--i;
- ret = darray_insert_item(&w->inodes, i, e);
+ ret = darray_insert_item(&w->inodes, i - w->inodes.data, new);
if (ret)
- return ret;
+ return ERR_PTR(ret);
}
return i;
}
+static struct inode_walker_entry *walk_inode(struct btree_trans *trans,
+ struct inode_walker *w, struct bpos pos)
+{
+ int ret = get_inodes_all_snapshots(trans, w, pos.inode);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return lookup_inode_for_snapshot(trans->c, w, pos.snapshot);
+}
+
static int __get_visible_inodes(struct btree_trans *trans,
struct inode_walker *w,
struct snapshots_seen *s,
if (IS_ERR(delete))
return PTR_ERR(delete);
- tmp = bch2_bkey_make_mut(trans, k);
+ tmp = bch2_bkey_make_mut_noupdate(trans, k);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
ret = hash_redo_key(trans, desc, hash_info, k_iter, hash_k);
- if (ret) {
+ if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
bch_err(c, "hash_redo_key err %s", bch2_err_str(ret));
+ if (ret)
return ret;
- }
ret = -BCH_ERR_transaction_restart_nested;
}
fsck_err:
iter->pos.snapshot),
POS(u.bi_inum, U64_MAX),
0, NULL);
- if (ret) {
+ if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
bch_err(c, "error in fsck: error truncating inode: %s",
bch2_err_str(ret));
+ if (ret)
return ret;
- }
/*
* We truncated without our normal sector accounting hook, just
err:
fsck_err:
if (ret)
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
noinline_for_stack
-static int check_inodes(struct bch_fs *c, bool full)
+int bch2_check_inodes(struct bch_fs *c)
{
+ bool full = c->opts.fsck;
struct btree_trans trans;
struct btree_iter iter;
struct bch_inode_unpacked prev = { 0 };
bch2_trans_exit(&trans);
snapshots_seen_exit(&s);
if (ret)
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
struct btree_iter *iter,
struct bpos pos)
{
- struct bkey_s_c k;
- int ret;
-
- bch2_trans_iter_init(trans, iter, BTREE_ID_dirents, pos, 0);
- k = bch2_btree_iter_peek_slot(iter);
- ret = bkey_err(k);
- if (!ret && k.k->type != KEY_TYPE_dirent)
- ret = -ENOENT;
- if (ret) {
- bch2_trans_iter_exit(trans, iter);
- return (struct bkey_s_c_dirent) { .k = ERR_PTR(ret) };
- }
-
- return bkey_s_c_to_dirent(k);
+ return bch2_bkey_get_iter_typed(trans, iter, BTREE_ID_dirents, pos, 0, dirent);
}
static bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
d = dirent_get_by_pos(trans, &iter,
SPOS(inode->bi_dir, inode->bi_dir_offset, snapshot));
- ret = bkey_err(d.s_c);
+ ret = bkey_err(d);
if (ret)
- return ret == -ENOENT ? 0 : ret;
+ return bch2_err_matches(ret, ENOENT) ? 0 : ret;
ret = dirent_points_to_inode(d, inode);
bch2_trans_iter_exit(trans, &iter);
}
fsck_err:
if (ret)
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
if (!ret && trans_was_restarted(trans, restart_count))
ret = -BCH_ERR_transaction_restart_nested;
return ret;
typedef DARRAY(struct extent_end) extent_ends;
+static int get_print_extent(struct btree_trans *trans, struct bpos pos, struct printbuf *out)
+{
+ struct btree_iter iter;
+ struct bkey_s_c k;
+ int ret;
+
+ k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_extents, pos,
+ BTREE_ITER_SLOTS|
+ BTREE_ITER_ALL_SNAPSHOTS|
+ BTREE_ITER_NOT_EXTENTS);
+ ret = bkey_err(k);
+ if (ret)
+ return ret;
+
+ bch2_bkey_val_to_text(out, trans->c, k);
+ bch2_trans_iter_exit(trans, &iter);
+ return 0;
+}
+
static int check_overlapping_extents(struct btree_trans *trans,
struct snapshots_seen *seen,
extent_ends *extent_ends,
i->snapshot, &i->seen))
continue;
- if (fsck_err_on(i->offset > bkey_start_offset(k.k), c,
- "overlapping extents: extent in snapshot %u ends at %llu overlaps with\n%s",
- i->snapshot,
- i->offset,
- (printbuf_reset(&buf),
- bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
+ if (i->offset <= bkey_start_offset(k.k))
+ continue;
+
+ printbuf_reset(&buf);
+ prt_str(&buf, "overlapping extents:\n ");
+ bch2_bkey_val_to_text(&buf, c, k);
+ prt_str(&buf, "\n ");
+
+ ret = get_print_extent(trans, SPOS(k.k->p.inode, i->offset, i->snapshot), &buf);
+ if (ret)
+ break;
+
+ if (fsck_err(c, "%s", buf.buf)) {
struct bkey_i *update = bch2_trans_kmalloc(trans, bkey_bytes(k.k));
if ((ret = PTR_ERR_OR_ZERO(update)))
goto err;
bkey_reassemble(update, k);
- ret = bch2_trans_update_extent(trans, iter, update, 0);
- if (!ret)
+ ret = bch2_trans_update_extent(trans, iter, update,
+ BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
+ if (ret)
goto err;
}
}
sizeof(seen->ids.data[0]) * seen->ids.size,
GFP_KERNEL);
if (!n.seen.ids.data)
- return -ENOMEM;
+ return -BCH_ERR_ENOMEM_fsck_extent_ends_at;
darray_for_each(*extent_ends, i) {
if (i->snapshot == k.k->p.snapshot) {
if (ret)
goto err;
- ret = __walk_inode(trans, inode, equiv);
- if (ret < 0)
+ i = walk_inode(trans, inode, equiv);
+ ret = PTR_ERR_OR_ZERO(i);
+ if (ret)
goto err;
- if (fsck_err_on(ret == INT_MAX, c,
+ if (fsck_err_on(!i, c,
"extent in missing inode:\n %s",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
goto out;
}
- if (ret == INT_MAX) {
- ret = 0;
+ if (!i)
goto out;
- }
-
- i = inode->inodes.data + ret;
- ret = 0;
if (fsck_err_on(!S_ISREG(i->inode.bi_mode) &&
!S_ISLNK(i->inode.bi_mode), c,
printbuf_exit(&buf);
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
* Walk extents: verify that extents have a corresponding S_ISREG inode, and
* that i_size an i_sectors are consistent
*/
-noinline_for_stack
-static int check_extents(struct bch_fs *c)
+int bch2_check_extents(struct bch_fs *c)
{
struct inode_walker w = inode_walker_init();
struct snapshots_seen s;
struct btree_iter iter;
struct bkey_s_c k;
extent_ends extent_ends = { 0 };
+ struct disk_reservation res = { 0 };
int ret = 0;
snapshots_seen_init(&s);
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
- bch_verbose(c, "checking extents");
-
ret = for_each_btree_key_commit(&trans, iter, BTREE_ID_extents,
POS(BCACHEFS_ROOT_INO, 0),
BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
- NULL, NULL,
- BTREE_INSERT_LAZY_RW|BTREE_INSERT_NOFAIL,
- check_extent(&trans, &iter, k, &w, &s, &extent_ends));
+ &res, NULL,
+ BTREE_INSERT_LAZY_RW|BTREE_INSERT_NOFAIL, ({
+ bch2_disk_reservation_put(c, &res);
+ check_extent(&trans, &iter, k, &w, &s, &extent_ends);
+ }));
+ bch2_disk_reservation_put(c, &res);
extent_ends_reset(&extent_ends);
darray_exit(&extent_ends);
inode_walker_exit(&w);
snapshots_seen_exit(&s);
if (ret)
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
}
fsck_err:
if (ret)
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
if (!ret && trans_was_restarted(trans, restart_count))
ret = -BCH_ERR_transaction_restart_nested;
return ret;
printbuf_exit(&buf);
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
BUG_ON(!iter->path->should_be_locked);
- ret = __walk_inode(trans, dir, equiv);
+ i = walk_inode(trans, dir, equiv);
+ ret = PTR_ERR_OR_ZERO(i);
if (ret < 0)
goto err;
- if (fsck_err_on(ret == INT_MAX, c,
+ if (dir->first_this_inode)
+ *hash_info = bch2_hash_info_init(c, &dir->inodes.data[0].inode);
+ dir->first_this_inode = false;
+
+ if (fsck_err_on(!i, c,
"dirent in nonexisting directory:\n%s",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
goto out;
}
- if (ret == INT_MAX) {
- ret = 0;
+ if (!i)
goto out;
- }
-
- i = dir->inodes.data + ret;
- ret = 0;
if (fsck_err_on(!S_ISDIR(i->inode.bi_mode), c,
"dirent in non directory inode type %s:\n%s",
goto out;
}
- if (dir->first_this_inode)
- *hash_info = bch2_hash_info_init(c, &dir->inodes.data[0].inode);
-
- ret = hash_check_key(trans, bch2_dirent_hash_desc,
- hash_info, iter, k);
+ ret = hash_check_key(trans, bch2_dirent_hash_desc, hash_info, iter, k);
if (ret < 0)
goto err;
if (ret) {
ret = __subvol_lookup(trans, target_subvol,
&target_snapshot, &target_inum);
- if (ret && ret != -ENOENT)
+ if (ret && !bch2_err_matches(ret, ENOENT))
goto err;
if (fsck_err_on(ret, c,
- "dirent points to missing subvolume %llu",
- le64_to_cpu(d.v->d_child_subvol))) {
+ "dirent points to missing subvolume %u",
+ le32_to_cpu(d.v->d_child_subvol))) {
ret = __remove_dirent(trans, d.k->p);
goto err;
}
ret = __lookup_inode(trans, target_inum,
&subvol_root, &target_snapshot);
- if (ret && ret != -ENOENT)
+ if (ret && !bch2_err_matches(ret, ENOENT))
goto err;
if (fsck_err_on(ret, c,
printbuf_exit(&buf);
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
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)
+int bch2_check_dirents(struct bch_fs *c)
{
struct inode_walker dir = inode_walker_init();
struct inode_walker target = inode_walker_init();
struct bkey_s_c k;
int ret = 0;
- bch_verbose(c, "checking dirents");
-
snapshots_seen_init(&s);
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
inode_walker_exit(&target);
if (ret)
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
struct inode_walker *inode)
{
struct bch_fs *c = trans->c;
+ struct inode_walker_entry *i;
int ret;
ret = check_key_has_snapshot(trans, iter, k);
if (ret)
return ret;
- ret = __walk_inode(trans, inode, k.k->p);
- if (ret < 0)
+ i = walk_inode(trans, inode, k.k->p);
+ ret = PTR_ERR_OR_ZERO(i);
+ if (ret)
return ret;
- if (fsck_err_on(ret == INT_MAX, c,
+ if (inode->first_this_inode)
+ *hash_info = bch2_hash_info_init(c, &inode->inodes.data[0].inode);
+ inode->first_this_inode = false;
+
+ if (fsck_err_on(!i, c,
"xattr for missing inode %llu",
k.k->p.inode))
return bch2_btree_delete_at(trans, iter, 0);
- if (ret == INT_MAX)
+ if (!i)
return 0;
- ret = 0;
-
- if (inode->first_this_inode)
- *hash_info = bch2_hash_info_init(c, &inode->inodes.data[0].inode);
-
ret = hash_check_key(trans, bch2_xattr_hash_desc, hash_info, iter, k);
fsck_err:
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
/*
* Walk xattrs: verify that they all have a corresponding inode
*/
-noinline_for_stack
-static int check_xattrs(struct bch_fs *c)
+int bch2_check_xattrs(struct bch_fs *c)
{
struct inode_walker inode = inode_walker_init();
struct bch_hash_info hash_info;
struct bkey_s_c k;
int ret = 0;
- bch_verbose(c, "checking xattrs");
-
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
ret = for_each_btree_key_commit(&trans, iter, BTREE_ID_xattrs,
bch2_trans_exit(&trans);
if (ret)
- bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
int ret;
ret = __subvol_lookup(trans, BCACHEFS_ROOT_SUBVOL, &snapshot, &inum);
- if (ret && ret != -ENOENT)
+ if (ret && !bch2_err_matches(ret, ENOENT))
return ret;
if (mustfix_fsck_err_on(ret, c, "root subvol missing")) {
}
ret = __lookup_inode(trans, BCACHEFS_ROOT_INO, &root_inode, &snapshot);
- if (ret && ret != -ENOENT)
+ if (ret && !bch2_err_matches(ret, ENOENT))
return ret;
if (mustfix_fsck_err_on(ret, c, "root directory missing") ||
}
/* Get root directory, create if it doesn't exist: */
-noinline_for_stack
-static int check_root(struct bch_fs *c)
+int bch2_check_root(struct bch_fs *c)
{
- bch_verbose(c, "checking root directory");
+ int ret;
- return bch2_trans_do(c, NULL, NULL,
+ ret = bch2_trans_do(c, NULL, NULL,
BTREE_INSERT_NOFAIL|
BTREE_INSERT_LAZY_RW,
check_root_trans(&trans));
+
+ if (ret)
+ bch_err_fn(c, ret);
+ return ret;
}
struct pathbuf_entry {
PTR_ERR_OR_ZERO((d = dirent_get_by_pos(trans, &dirent_iter,
SPOS(inode->bi_dir, inode->bi_dir_offset,
parent_snapshot))).k));
- if (ret && ret != -ENOENT)
+ if (ret && !bch2_err_matches(ret, ENOENT))
break;
if (!ret && !dirent_points_to_inode(d, inode)) {
bch2_trans_iter_exit(trans, &dirent_iter);
- ret = -ENOENT;
+ ret = -BCH_ERR_ENOENT_dirent_doesnt_match_inode;
}
- if (ret == -ENOENT) {
+ if (bch2_err_matches(ret, ENOENT)) {
if (fsck_err(c, "unreachable inode %llu:%u, type %s nlink %u backptr %llu:%llu",
inode->bi_inum, snapshot,
bch2_d_type_str(inode_d_type(inode)),
}
fsck_err:
if (ret)
- bch_err(c, "%s: err %s", __func__, bch2_err_str(ret));
+ bch_err_fn(c, ret);
return ret;
}
/*
* Check for unreachable inodes, as well as loops in the directory structure:
- * After check_dirents(), if an inode backpointer doesn't exist that means it's
+ * After bch2_check_dirents(), if an inode backpointer doesn't exist that means it's
* unreachable:
*/
-noinline_for_stack
-static int check_directory_structure(struct bch_fs *c)
+int bch2_check_directory_structure(struct bch_fs *c)
{
struct btree_trans trans;
struct btree_iter iter;
break;
}
bch2_trans_iter_exit(&trans, &iter);
-
+ bch2_trans_exit(&trans);
darray_exit(&path);
- bch2_trans_exit(&trans);
+ if (ret)
+ bch_err_fn(c, ret);
return ret;
}
if (!d) {
bch_err(c, "fsck: error allocating memory for nlink_table, size %zu",
new_size);
- return -ENOMEM;
+ return -BCH_ERR_ENOMEM_fsck_add_nlink;
}
if (t->d)
* Backpointer and directory structure checks are sufficient for
* directories, since they can't have hardlinks:
*/
- if (S_ISDIR(le16_to_cpu(u.bi_mode)))
+ if (S_ISDIR(u.bi_mode))
continue;
if (!u.bi_nlink)
BUG_ON(bch2_inode_unpack(k, &u));
- if (S_ISDIR(le16_to_cpu(u.bi_mode)))
+ if (S_ISDIR(u.bi_mode))
return 0;
if (!u.bi_nlink)
return 0;
}
-noinline_for_stack
-static int check_nlinks(struct bch_fs *c)
+int bch2_check_nlinks(struct bch_fs *c)
{
struct nlink_table links = { 0 };
u64 this_iter_range_start, next_iter_range_start = 0;
int ret = 0;
- bch_verbose(c, "checking inode nlinks");
-
do {
this_iter_range_start = next_iter_range_start;
next_iter_range_start = U64_MAX;
kvfree(links.d);
+ if (ret)
+ bch_err_fn(c, ret);
return ret;
}
return bch2_trans_update(trans, iter, &u->k_i, BTREE_TRIGGER_NORUN);
}
-noinline_for_stack
-static int fix_reflink_p(struct bch_fs *c)
+int bch2_fix_reflink_p(struct bch_fs *c)
{
- struct btree_trans trans;
struct btree_iter iter;
struct bkey_s_c k;
int ret;
if (c->sb.version >= bcachefs_metadata_version_reflink_p_fix)
return 0;
- bch_verbose(c, "fixing reflink_p keys");
-
- bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
-
- ret = for_each_btree_key_commit(&trans, iter,
- BTREE_ID_extents, POS_MIN,
- BTREE_ITER_INTENT|BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
- NULL, NULL, BTREE_INSERT_NOFAIL|BTREE_INSERT_LAZY_RW,
- fix_reflink_p_key(&trans, &iter, k));
-
- bch2_trans_exit(&trans);
- return ret;
-}
-
-/*
- * Checks for inconsistencies that shouldn't happen, unless we have a bug.
- * Doesn't fix them yet, mainly because they haven't yet been observed:
- */
-int bch2_fsck_full(struct bch_fs *c)
-{
- int ret;
-again:
- ret = bch2_fs_check_snapshots(c) ?:
- bch2_fs_check_subvols(c) ?:
- bch2_delete_dead_snapshots(c) ?:
- check_inodes(c, true) ?:
- check_extents(c) ?:
- check_dirents(c) ?:
- check_xattrs(c) ?:
- check_root(c) ?:
- check_directory_structure(c) ?:
- check_nlinks(c) ?:
- fix_reflink_p(c);
-
- if (bch2_err_matches(ret, BCH_ERR_need_snapshot_cleanup)) {
- set_bit(BCH_FS_HAVE_DELETED_SNAPSHOTS, &c->flags);
- goto again;
- }
+ ret = bch2_trans_run(c,
+ for_each_btree_key_commit(&trans, iter,
+ BTREE_ID_extents, POS_MIN,
+ BTREE_ITER_INTENT|BTREE_ITER_PREFETCH|
+ BTREE_ITER_ALL_SNAPSHOTS, k,
+ NULL, NULL, BTREE_INSERT_NOFAIL|BTREE_INSERT_LAZY_RW,
+ fix_reflink_p_key(&trans, &iter, k)));
+ if (ret)
+ bch_err_fn(c, ret);
return ret;
}
-
-int bch2_fsck_walk_inodes_only(struct bch_fs *c)
-{
- return bch2_fs_check_snapshots(c) ?:
- bch2_fs_check_subvols(c) ?:
- bch2_delete_dead_snapshots(c) ?:
- check_inodes(c, false);
-}