Commit b65db750 authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: Enumerate fsck errors



This patch adds a superblock error counter for every distinct fsck
error; this means that when analyzing filesystems out in the wild we'll
be able to see what sorts of inconsistencies are being found and repair,
and hence what bugs to look for.

Errors validating bkeys are not yet considered distinct fsck errors, but
this patch adds a new helper, bkey_fsck_err(), in order to add distinct
error types for them as well.

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent f5d26fa3
Loading
Loading
Loading
Loading
+84 −74
Original line number Diff line number Diff line
@@ -192,114 +192,109 @@ static unsigned bch_alloc_v1_val_u64s(const struct bch_alloc *a)
	return DIV_ROUND_UP(bytes, sizeof(u64));
}

int bch2_alloc_v1_invalid(const struct bch_fs *c, struct bkey_s_c k,
int bch2_alloc_v1_invalid(struct bch_fs *c, struct bkey_s_c k,
			  enum bkey_invalid_flags flags,
			  struct printbuf *err)
{
	struct bkey_s_c_alloc a = bkey_s_c_to_alloc(k);
	int ret = 0;

	/* allow for unknown fields */
	if (bkey_val_u64s(a.k) < bch_alloc_v1_val_u64s(a.v)) {
		prt_printf(err, "incorrect value size (%zu < %u)",
	bkey_fsck_err_on(bkey_val_u64s(a.k) < bch_alloc_v1_val_u64s(a.v), c, err,
			 alloc_v1_val_size_bad,
			 "incorrect value size (%zu < %u)",
			 bkey_val_u64s(a.k), bch_alloc_v1_val_u64s(a.v));
		return -BCH_ERR_invalid_bkey;
	}

	return 0;
fsck_err:
	return ret;
}

int bch2_alloc_v2_invalid(const struct bch_fs *c, struct bkey_s_c k,
int bch2_alloc_v2_invalid(struct bch_fs *c, struct bkey_s_c k,
			  enum bkey_invalid_flags flags,
			  struct printbuf *err)
{
	struct bkey_alloc_unpacked u;
	int ret = 0;

	if (bch2_alloc_unpack_v2(&u, k)) {
		prt_printf(err, "unpack error");
		return -BCH_ERR_invalid_bkey;
	}

	return 0;
	bkey_fsck_err_on(bch2_alloc_unpack_v2(&u, k), c, err,
			 alloc_v2_unpack_error,
			 "unpack error");
fsck_err:
	return ret;
}

int bch2_alloc_v3_invalid(const struct bch_fs *c, struct bkey_s_c k,
int bch2_alloc_v3_invalid(struct bch_fs *c, struct bkey_s_c k,
			  enum bkey_invalid_flags flags,
			  struct printbuf *err)
{
	struct bkey_alloc_unpacked u;
	int ret = 0;

	if (bch2_alloc_unpack_v3(&u, k)) {
		prt_printf(err, "unpack error");
		return -BCH_ERR_invalid_bkey;
	}

	return 0;
	bkey_fsck_err_on(bch2_alloc_unpack_v3(&u, k), c, err,
			 alloc_v2_unpack_error,
			 "unpack error");
fsck_err:
	return ret;
}

int bch2_alloc_v4_invalid(const struct bch_fs *c, struct bkey_s_c k,
int bch2_alloc_v4_invalid(struct bch_fs *c, struct bkey_s_c k,
			  enum bkey_invalid_flags flags, struct printbuf *err)
{
	struct bkey_s_c_alloc_v4 a = bkey_s_c_to_alloc_v4(k);
	int ret = 0;

	if (alloc_v4_u64s(a.v) > bkey_val_u64s(k.k)) {
		prt_printf(err, "bad val size (%u > %zu)",
	bkey_fsck_err_on(alloc_v4_u64s(a.v) > bkey_val_u64s(k.k), c, err,
			 alloc_v4_val_size_bad,
			 "bad val size (%u > %zu)",
			 alloc_v4_u64s(a.v), bkey_val_u64s(k.k));
		return -BCH_ERR_invalid_bkey;
	}

	if (!BCH_ALLOC_V4_BACKPOINTERS_START(a.v) &&
	    BCH_ALLOC_V4_NR_BACKPOINTERS(a.v)) {
		prt_printf(err, "invalid backpointers_start");
		return -BCH_ERR_invalid_bkey;
	}
	bkey_fsck_err_on(!BCH_ALLOC_V4_BACKPOINTERS_START(a.v) &&
			 BCH_ALLOC_V4_NR_BACKPOINTERS(a.v), c, err,
			 alloc_v4_backpointers_start_bad,
			 "invalid backpointers_start");

	if (alloc_data_type(*a.v, a.v->data_type) != a.v->data_type) {
		prt_printf(err, "invalid data type (got %u should be %u)",
	bkey_fsck_err_on(alloc_data_type(*a.v, a.v->data_type) != a.v->data_type, c, err,
			 alloc_key_data_type_bad,
			 "invalid data type (got %u should be %u)",
			 a.v->data_type, alloc_data_type(*a.v, a.v->data_type));
		return -BCH_ERR_invalid_bkey;
	}

	switch (a.v->data_type) {
	case BCH_DATA_free:
	case BCH_DATA_need_gc_gens:
	case BCH_DATA_need_discard:
		if (a.v->dirty_sectors ||
		bkey_fsck_err_on(a.v->dirty_sectors ||
				 a.v->cached_sectors ||
		    a.v->stripe) {
			prt_printf(err, "empty data type free but have data");
			return -BCH_ERR_invalid_bkey;
		}
				 a.v->stripe, c, err,
				 alloc_key_empty_but_have_data,
				 "empty data type free but have data");
		break;
	case BCH_DATA_sb:
	case BCH_DATA_journal:
	case BCH_DATA_btree:
	case BCH_DATA_user:
	case BCH_DATA_parity:
		if (!a.v->dirty_sectors) {
			prt_printf(err, "data_type %s but dirty_sectors==0",
		bkey_fsck_err_on(!a.v->dirty_sectors, c, err,
				 alloc_key_dirty_sectors_0,
				 "data_type %s but dirty_sectors==0",
				 bch2_data_types[a.v->data_type]);
			return -BCH_ERR_invalid_bkey;
		}
		break;
	case BCH_DATA_cached:
		if (!a.v->cached_sectors ||
		bkey_fsck_err_on(!a.v->cached_sectors ||
				 a.v->dirty_sectors ||
		    a.v->stripe) {
			prt_printf(err, "data type inconsistency");
			return -BCH_ERR_invalid_bkey;
		}

		if (!a.v->io_time[READ] &&
		    c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_to_lru_refs) {
			prt_printf(err, "cached bucket with read_time == 0");
			return -BCH_ERR_invalid_bkey;
		}
				 a.v->stripe, c, err,
				 alloc_key_cached_inconsistency,
				 "data type inconsistency");

		bkey_fsck_err_on(!a.v->io_time[READ] &&
				 c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_to_lru_refs,
				 c, err,
				 alloc_key_cached_but_read_time_zero,
				 "cached bucket with read_time == 0");
		break;
	case BCH_DATA_stripe:
		break;
	}

	return 0;
fsck_err:
	return ret;
}

static inline u64 swab40(u64 x)
@@ -521,17 +516,18 @@ static unsigned alloc_gen(struct bkey_s_c k, unsigned offset)
		: 0;
}

int bch2_bucket_gens_invalid(const struct bch_fs *c, struct bkey_s_c k,
int bch2_bucket_gens_invalid(struct bch_fs *c, struct bkey_s_c k,
			     enum bkey_invalid_flags flags,
			     struct printbuf *err)
{
	if (bkey_val_bytes(k.k) != sizeof(struct bch_bucket_gens)) {
		prt_printf(err, "bad val size (%zu != %zu)",
		       bkey_val_bytes(k.k), sizeof(struct bch_bucket_gens));
		return -BCH_ERR_invalid_bkey;
	}
	int ret = 0;

	return 0;
	bkey_fsck_err_on(bkey_val_bytes(k.k) != sizeof(struct bch_bucket_gens), c, err,
			 bucket_gens_val_size_bad,
			 "bad val size (%zu != %zu)",
			 bkey_val_bytes(k.k), sizeof(struct bch_bucket_gens));
fsck_err:
	return ret;
}

void bch2_bucket_gens_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k)
@@ -986,6 +982,7 @@ int bch2_check_alloc_key(struct btree_trans *trans,
	int ret;

	if (fsck_err_on(!bch2_dev_bucket_exists(c, alloc_k.k->p), c,
			alloc_key_to_missing_dev_bucket,
			"alloc key for invalid device:bucket %llu:%llu",
			alloc_k.k->p.inode, alloc_k.k->p.offset))
		return bch2_btree_delete_at(trans, alloc_iter, 0);
@@ -1005,7 +1002,8 @@ int bch2_check_alloc_key(struct btree_trans *trans,

	if (k.k->type != discard_key_type &&
	    (c->opts.reconstruct_alloc ||
	     fsck_err(c, "incorrect key in need_discard btree (got %s should be %s)\n"
	     fsck_err(c, need_discard_key_wrong,
		      "incorrect key in need_discard btree (got %s should be %s)\n"
		      "  %s",
		      bch2_bkey_types[k.k->type],
		      bch2_bkey_types[discard_key_type],
@@ -1035,7 +1033,8 @@ int bch2_check_alloc_key(struct btree_trans *trans,

	if (k.k->type != freespace_key_type &&
	    (c->opts.reconstruct_alloc ||
	     fsck_err(c, "incorrect key in freespace btree (got %s should be %s)\n"
	     fsck_err(c, freespace_key_wrong,
		      "incorrect key in freespace btree (got %s should be %s)\n"
		      "  %s",
		      bch2_bkey_types[k.k->type],
		      bch2_bkey_types[freespace_key_type],
@@ -1066,7 +1065,8 @@ int bch2_check_alloc_key(struct btree_trans *trans,

	if (a->gen != alloc_gen(k, gens_offset) &&
	    (c->opts.reconstruct_alloc ||
	     fsck_err(c, "incorrect gen in bucket_gens btree (got %u should be %u)\n"
	     fsck_err(c, bucket_gens_key_wrong,
		      "incorrect gen in bucket_gens btree (got %u should be %u)\n"
		      "  %s",
		      alloc_gen(k, gens_offset), a->gen,
		      (printbuf_reset(&buf),
@@ -1124,7 +1124,8 @@ int bch2_check_alloc_hole_freespace(struct btree_trans *trans,

	if (k.k->type != KEY_TYPE_set &&
	    (c->opts.reconstruct_alloc ||
	     fsck_err(c, "hole in alloc btree missing in freespace btree\n"
	     fsck_err(c, freespace_hole_missing,
		      "hole in alloc btree missing in freespace btree\n"
		      "  device %llu buckets %llu-%llu",
		      freespace_iter->pos.inode,
		      freespace_iter->pos.offset,
@@ -1187,6 +1188,7 @@ int bch2_check_alloc_hole_bucket_gens(struct btree_trans *trans,

		for (i = gens_offset; i < gens_end_offset; i++) {
			if (fsck_err_on(g.v.gens[i], c,
					bucket_gens_hole_wrong,
					"hole in alloc btree at %llu:%llu with nonzero gen in bucket_gens btree (%u)",
					bucket_gens_pos_to_alloc(k.k->p, i).inode,
					bucket_gens_pos_to_alloc(k.k->p, i).offset,
@@ -1244,6 +1246,7 @@ static noinline_for_stack int __bch2_check_discard_freespace_key(struct btree_tr
		return ret;

	if (fsck_err_on(!bch2_dev_bucket_exists(c, pos), c,
			need_discard_freespace_key_to_invalid_dev_bucket,
			"entry in %s btree for nonexistant dev:bucket %llu:%llu",
			bch2_btree_id_str(iter->btree_id), pos.inode, pos.offset))
		goto delete;
@@ -1253,6 +1256,7 @@ static noinline_for_stack int __bch2_check_discard_freespace_key(struct btree_tr
	if (fsck_err_on(a->data_type != state ||
			(state == BCH_DATA_free &&
			 genbits != alloc_freespace_genbits(*a)), c,
			need_discard_freespace_key_bad,
			"%s\n  incorrectly set at %s:%llu:%llu:0 (free %u, genbits %llu should be %llu)",
			(bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf),
			bch2_btree_id_str(iter->btree_id),
@@ -1320,6 +1324,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
	dev_exists = bch2_dev_exists2(c, k.k->p.inode);
	if (!dev_exists) {
		if (fsck_err_on(!dev_exists, c,
				bucket_gens_to_invalid_dev,
				"bucket_gens key for invalid device:\n  %s",
				(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
			ret = bch2_btree_delete_at(trans, iter, 0);
@@ -1330,6 +1335,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
	ca = bch_dev_bkey_exists(c, k.k->p.inode);
	if (fsck_err_on(end <= ca->mi.first_bucket ||
			start >= ca->mi.nbuckets, c,
			bucket_gens_to_invalid_buckets,
			"bucket_gens key for invalid buckets:\n  %s",
			(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
		ret = bch2_btree_delete_at(trans, iter, 0);
@@ -1338,6 +1344,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,

	for (b = start; b < ca->mi.first_bucket; b++)
		if (fsck_err_on(g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK], c,
				bucket_gens_nonzero_for_invalid_buckets,
				"bucket_gens key has nonzero gen for invalid bucket")) {
			g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK] = 0;
			need_update = true;
@@ -1345,6 +1352,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,

	for (b = ca->mi.nbuckets; b < end; b++)
		if (fsck_err_on(g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK], c,
				bucket_gens_nonzero_for_invalid_buckets,
				"bucket_gens key has nonzero gen for invalid bucket")) {
			g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK] = 0;
			need_update = true;
@@ -1495,11 +1503,13 @@ static int bch2_check_alloc_to_lru_ref(struct btree_trans *trans,
		return ret;

	if (fsck_err_on(!a->io_time[READ], c,
			alloc_key_cached_but_read_time_zero,
			"cached bucket with read_time 0\n"
			"  %s",
		(printbuf_reset(&buf),
		 bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf)) ||
	    fsck_err_on(lru_k.k->type != KEY_TYPE_set, c,
			alloc_key_to_missing_lru_entry,
			"missing lru entry\n"
			"  %s",
			(printbuf_reset(&buf),
+5 −5
Original line number Diff line number Diff line
@@ -149,13 +149,13 @@ struct bkey_i_alloc_v4 *bch2_alloc_to_v4_mut(struct btree_trans *, struct bkey_s

int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int);

int bch2_alloc_v1_invalid(const struct bch_fs *, struct bkey_s_c,
int bch2_alloc_v1_invalid(struct bch_fs *, struct bkey_s_c,
			  enum bkey_invalid_flags, struct printbuf *);
int bch2_alloc_v2_invalid(const struct bch_fs *, struct bkey_s_c,
int bch2_alloc_v2_invalid(struct bch_fs *, struct bkey_s_c,
			  enum bkey_invalid_flags, struct printbuf *);
int bch2_alloc_v3_invalid(const struct bch_fs *, struct bkey_s_c,
int bch2_alloc_v3_invalid(struct bch_fs *, struct bkey_s_c,
			  enum bkey_invalid_flags, struct printbuf *);
int bch2_alloc_v4_invalid(const struct bch_fs *, struct bkey_s_c,
int bch2_alloc_v4_invalid(struct bch_fs *, struct bkey_s_c,
			  enum bkey_invalid_flags, struct printbuf *);
void bch2_alloc_v4_swab(struct bkey_s);
void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
@@ -193,7 +193,7 @@ void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
	.min_val_size	= 48,				\
})

int bch2_bucket_gens_invalid(const struct bch_fs *, struct bkey_s_c,
int bch2_bucket_gens_invalid(struct bch_fs *, struct bkey_s_c,
			     enum bkey_invalid_flags, struct printbuf *);
void bch2_bucket_gens_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);

+12 −8
Original line number Diff line number Diff line
@@ -37,19 +37,20 @@ static bool extent_matches_bp(struct bch_fs *c,
	return false;
}

int bch2_backpointer_invalid(const struct bch_fs *c, struct bkey_s_c k,
int bch2_backpointer_invalid(struct bch_fs *c, struct bkey_s_c k,
			     enum bkey_invalid_flags flags,
			     struct printbuf *err)
{
	struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(k);
	struct bpos bucket = bp_pos_to_bucket(c, bp.k->p);
	int ret = 0;

	if (!bpos_eq(bp.k->p, bucket_pos_to_bp(c, bucket, bp.v->bucket_offset))) {
		prt_str(err, "backpointer at wrong pos");
		return -BCH_ERR_invalid_bkey;
	}

	return 0;
	bkey_fsck_err_on(!bpos_eq(bp.k->p, bucket_pos_to_bp(c, bucket, bp.v->bucket_offset)),
			 c, err,
			 backpointer_pos_wrong,
			 "backpointer at wrong pos");
fsck_err:
	return ret;
}

void bch2_backpointer_to_text(struct printbuf *out, const struct bch_backpointer *bp)
@@ -356,6 +357,7 @@ static int bch2_check_btree_backpointer(struct btree_trans *trans, struct btree_
	int ret = 0;

	if (fsck_err_on(!bch2_dev_exists2(c, k.k->p.inode), c,
			backpointer_to_missing_device,
			"backpointer for missing device:\n%s",
			(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
		ret = bch2_btree_delete_at(trans, bp_iter, 0);
@@ -369,6 +371,7 @@ static int bch2_check_btree_backpointer(struct btree_trans *trans, struct btree_
		goto out;

	if (fsck_err_on(alloc_k.k->type != KEY_TYPE_alloc_v4, c,
			backpointer_to_missing_alloc,
			"backpointer for nonexistent alloc key: %llu:%llu:0\n%s",
			alloc_iter.pos.inode, alloc_iter.pos.offset,
			(bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf))) {
@@ -460,7 +463,7 @@ static int check_bp_exists(struct btree_trans *trans,

	if (c->sb.version_upgrade_complete < bcachefs_metadata_version_backpointers ||
	    c->opts.reconstruct_alloc ||
	    fsck_err(c, "%s", buf.buf))
	    fsck_err(c, ptr_to_missing_backpointer, "%s", buf.buf))
		ret = bch2_bucket_backpointer_mod(trans, bucket, bp, orig_k, true);

	goto out;
@@ -793,6 +796,7 @@ static int check_one_backpointer(struct btree_trans *trans,
	}

	if (fsck_err_on(!k.k, c,
			backpointer_to_missing_ptr,
			"backpointer for missing extent\n  %s",
			(bch2_bkey_val_to_text(&buf, c, bp.s_c), buf.buf))) {
		ret = bch2_btree_delete_at_buffered(trans, BTREE_ID_backpointers, bp.k->p);
+1 −1
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@
#include "buckets.h"
#include "super.h"

int bch2_backpointer_invalid(const struct bch_fs *, struct bkey_s_c k,
int bch2_backpointer_invalid(struct bch_fs *, struct bkey_s_c k,
			     enum bkey_invalid_flags, struct printbuf *);
void bch2_backpointer_to_text(struct printbuf *, const struct bch_backpointer *);
void bch2_backpointer_k_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
+66 −81
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ const char * const bch2_bkey_types[] = {
	NULL
};

static int deleted_key_invalid(const struct bch_fs *c, struct bkey_s_c k,
static int deleted_key_invalid(struct bch_fs *c, struct bkey_s_c k,
			       enum bkey_invalid_flags flags, struct printbuf *err)
{
	return 0;
@@ -40,23 +40,24 @@ static int deleted_key_invalid(const struct bch_fs *c, struct bkey_s_c k,
	.key_invalid = deleted_key_invalid,		\
})

static int empty_val_key_invalid(const struct bch_fs *c, struct bkey_s_c k,
static int empty_val_key_invalid(struct bch_fs *c, struct bkey_s_c k,
				 enum bkey_invalid_flags flags, struct printbuf *err)
{
	if (bkey_val_bytes(k.k)) {
		prt_printf(err, "incorrect value size (%zu != 0)",
		       bkey_val_bytes(k.k));
		return -BCH_ERR_invalid_bkey;
	}
	int ret = 0;

	return 0;
	bkey_fsck_err_on(bkey_val_bytes(k.k), c, err,
			 bkey_val_size_nonzero,
			 "incorrect value size (%zu != 0)",
			 bkey_val_bytes(k.k));
fsck_err:
	return ret;
}

#define bch2_bkey_ops_error ((struct bkey_ops) {	\
	.key_invalid = empty_val_key_invalid,		\
})

static int key_type_cookie_invalid(const struct bch_fs *c, struct bkey_s_c k,
static int key_type_cookie_invalid(struct bch_fs *c, struct bkey_s_c k,
				   enum bkey_invalid_flags flags, struct printbuf *err)
{
	return 0;
@@ -71,7 +72,7 @@ static int key_type_cookie_invalid(const struct bch_fs *c, struct bkey_s_c k,
	.key_invalid = empty_val_key_invalid,		\
})

static int key_type_inline_data_invalid(const struct bch_fs *c, struct bkey_s_c k,
static int key_type_inline_data_invalid(struct bch_fs *c, struct bkey_s_c k,
					enum bkey_invalid_flags flags, struct printbuf *err)
{
	return 0;
@@ -92,18 +93,6 @@ static void key_type_inline_data_to_text(struct printbuf *out, struct bch_fs *c,
	.val_to_text	= key_type_inline_data_to_text,	\
})

static int key_type_set_invalid(const struct bch_fs *c, struct bkey_s_c k,
				enum bkey_invalid_flags flags, struct printbuf *err)
{
	if (bkey_val_bytes(k.k)) {
		prt_printf(err, "incorrect value size (%zu != %zu)",
		       bkey_val_bytes(k.k), sizeof(struct bch_cookie));
		return -BCH_ERR_invalid_bkey;
	}

	return 0;
}

static bool key_type_set_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r)
{
	bch2_key_resize(l.k, l.k->size + r.k->size);
@@ -111,7 +100,7 @@ static bool key_type_set_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_
}

#define bch2_bkey_ops_set ((struct bkey_ops) {		\
	.key_invalid	= key_type_set_invalid,		\
	.key_invalid	= empty_val_key_invalid,	\
	.key_merge	= key_type_set_merge,		\
})

@@ -129,17 +118,19 @@ int bch2_bkey_val_invalid(struct bch_fs *c, struct bkey_s_c k,
			  struct printbuf *err)
{
	const struct bkey_ops *ops = bch2_bkey_type_ops(k.k->type);
	int ret = 0;

	if (bkey_val_bytes(k.k) < ops->min_val_size) {
		prt_printf(err, "bad val size (%zu < %u)",
	bkey_fsck_err_on(bkey_val_bytes(k.k) < ops->min_val_size, c, err,
			 bkey_val_size_too_small,
			 "bad val size (%zu < %u)",
			 bkey_val_bytes(k.k), ops->min_val_size);
		return -BCH_ERR_invalid_bkey;
	}

	if (!ops->key_invalid)
		return 0;

	return ops->key_invalid(c, k, flags, err);
	ret = ops->key_invalid(c, k, flags, err);
fsck_err:
	return ret;
}

static u64 bch2_key_types_allowed[] = {
@@ -162,61 +153,55 @@ int __bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
			enum bkey_invalid_flags flags,
			struct printbuf *err)
{
	if (k.k->u64s < BKEY_U64s) {
		prt_printf(err, "u64s too small (%u < %zu)", k.k->u64s, BKEY_U64s);
		return -BCH_ERR_invalid_bkey;
	}
	int ret = 0;

	bkey_fsck_err_on(k.k->u64s < BKEY_U64s, c, err,
			 bkey_u64s_too_small,
			 "u64s too small (%u < %zu)", k.k->u64s, BKEY_U64s);

	if (type >= BKEY_TYPE_NR)
		return 0;

	if (flags & BKEY_INVALID_COMMIT	 &&
	    !(bch2_key_types_allowed[type] & BIT_ULL(k.k->type))) {
		prt_printf(err, "invalid key type for btree %s (%s)",
	bkey_fsck_err_on((flags & BKEY_INVALID_COMMIT) &&
			 !(bch2_key_types_allowed[type] & BIT_ULL(k.k->type)), c, err,
			 bkey_invalid_type_for_btree,
			 "invalid key type for btree %s (%s)",
			 bch2_btree_node_type_str(type), bch2_bkey_types[k.k->type]);
		return -BCH_ERR_invalid_bkey;
	}

	if (btree_node_type_is_extents(type) && !bkey_whiteout(k.k)) {
		if (k.k->size == 0) {
			prt_printf(err, "size == 0");
			return -BCH_ERR_invalid_bkey;
		}
		bkey_fsck_err_on(k.k->size == 0, c, err,
				 bkey_extent_size_zero,
				 "size == 0");

		if (k.k->size > k.k->p.offset) {
			prt_printf(err, "size greater than offset (%u > %llu)",
		bkey_fsck_err_on(k.k->size > k.k->p.offset, c, err,
				 bkey_extent_size_greater_than_offset,
				 "size greater than offset (%u > %llu)",
				 k.k->size, k.k->p.offset);
			return -BCH_ERR_invalid_bkey;
		}
	} else {
		if (k.k->size) {
			prt_printf(err, "size != 0");
			return -BCH_ERR_invalid_bkey;
		}
		bkey_fsck_err_on(k.k->size, c, err,
				 bkey_size_nonzero,
				 "size != 0");
	}

	if (type != BKEY_TYPE_btree) {
		enum btree_id btree = type - 1;

		if (!btree_type_has_snapshots(btree) &&
		    k.k->p.snapshot) {
			prt_printf(err, "nonzero snapshot");
			return -BCH_ERR_invalid_bkey;
		}
		bkey_fsck_err_on(!btree_type_has_snapshots(btree) &&
				 k.k->p.snapshot, c, err,
				 bkey_snapshot_nonzero,
				 "nonzero snapshot");

		if (btree_type_has_snapshots(btree) &&
		    !k.k->p.snapshot) {
			prt_printf(err, "snapshot == 0");
			return -BCH_ERR_invalid_bkey;
		}
		bkey_fsck_err_on(btree_type_has_snapshots(btree) &&
				 !k.k->p.snapshot, c, err,
				 bkey_snapshot_zero,
				 "snapshot == 0");

		if (bkey_eq(k.k->p, POS_MAX)) {
			prt_printf(err, "key at POS_MAX");
			return -BCH_ERR_invalid_bkey;
		bkey_fsck_err_on(bkey_eq(k.k->p, POS_MAX), c, err,
				 bkey_at_pos_max,
				 "key at POS_MAX");
	}
	}

	return 0;
fsck_err:
	return ret;
}

int bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
@@ -228,20 +213,20 @@ int bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
		bch2_bkey_val_invalid(c, k, flags, err);
}

int bch2_bkey_in_btree_node(struct btree *b, struct bkey_s_c k,
			    struct printbuf *err)
int bch2_bkey_in_btree_node(struct bch_fs *c, struct btree *b,
			    struct bkey_s_c k, struct printbuf *err)
{
	if (bpos_lt(k.k->p, b->data->min_key)) {
		prt_printf(err, "key before start of btree node");
		return -BCH_ERR_invalid_bkey;
	}
	int ret = 0;

	if (bpos_gt(k.k->p, b->data->max_key)) {
		prt_printf(err, "key past end of btree node");
		return -BCH_ERR_invalid_bkey;
	}
	bkey_fsck_err_on(bpos_lt(k.k->p, b->data->min_key), c, err,
			 bkey_before_start_of_btree_node,
			 "key before start of btree node");

	return 0;
	bkey_fsck_err_on(bpos_gt(k.k->p, b->data->max_key), c, err,
			 bkey_after_end_of_btree_node,
			 "key past end of btree node");
fsck_err:
	return ret;
}

void bch2_bpos_to_text(struct printbuf *out, struct bpos pos)
Loading