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,
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;
}
}
/*
- * 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);
}));
}
-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)
- 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,
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;
}
}
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);
+ ret = bch2_trans_update_extent(trans, iter, update,
+ BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
if (ret)
goto err;
}
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;
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,
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;
*hash_info = bch2_hash_info_init(c, &dir->inodes.data[0].inode);
dir->first_this_inode = false;
- if (fsck_err_on(ret == INT_MAX, c,
+ 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 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;
}
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 (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(ret == INT_MAX, c,
+ 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;
-
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;
}
}
/* 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 {
}
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;
}
* 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_snapshot_trees(c);
- 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);
-}