mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-22 00:00:03 +03:00
Update bcachefs sources to 7250b2ee5574 bcachefs: Fix deleted inodes btree in snapshot deletion
This commit is contained in:
parent
9799b119c3
commit
8eaf8ddf45
@ -1 +1 @@
|
||||
0d63ed13ea3d867055ae5752e2e0514a227d1dcb
|
||||
7250b2ee5574d7e2063c4498dde98ba11e2fd35f
|
||||
|
@ -276,7 +276,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
SET_BCH_SB_ENCRYPTION_TYPE(sb.sb, 1);
|
||||
}
|
||||
|
||||
bch_members_cpy_v2_v1(&sb);
|
||||
bch2_sb_members_cpy_v2_v1(&sb);
|
||||
|
||||
for (i = devs; i < devs + nr_devs; i++) {
|
||||
u64 size_sectors = i->size >> 9;
|
||||
|
@ -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_val_u64s(a.k), bch_alloc_v1_val_u64s(a.v));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
return 0;
|
||||
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));
|
||||
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)",
|
||||
alloc_v4_u64s(a.v), bkey_val_u64s(k.k));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
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));
|
||||
|
||||
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)",
|
||||
a.v->data_type, alloc_data_type(*a.v, a.v->data_type));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
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));
|
||||
|
||||
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 ||
|
||||
a.v->cached_sectors ||
|
||||
a.v->stripe) {
|
||||
prt_printf(err, "empty data type free but have data");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(a.v->dirty_sectors ||
|
||||
a.v->cached_sectors ||
|
||||
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",
|
||||
bch2_data_types[a.v->data_type]);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
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]);
|
||||
break;
|
||||
case BCH_DATA_cached:
|
||||
if (!a.v->cached_sectors ||
|
||||
a.v->dirty_sectors ||
|
||||
a.v->stripe) {
|
||||
prt_printf(err, "data type inconsistency");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(!a.v->cached_sectors ||
|
||||
a.v->dirty_sectors ||
|
||||
a.v->stripe, c, err,
|
||||
alloc_key_cached_inconsistency,
|
||||
"data type inconsistency");
|
||||
|
||||
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;
|
||||
}
|
||||
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),
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 @@ missing:
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -209,6 +209,7 @@
|
||||
#include "nocow_locking_types.h"
|
||||
#include "opts.h"
|
||||
#include "recovery_types.h"
|
||||
#include "sb-errors_types.h"
|
||||
#include "seqmutex.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -502,6 +503,8 @@ struct bch_dev {
|
||||
* Committed by bch2_write_super() -> bch_fs_mi_update()
|
||||
*/
|
||||
struct bch_member_cpu mi;
|
||||
atomic64_t errors[BCH_MEMBER_ERROR_NR];
|
||||
|
||||
__uuid_t uuid;
|
||||
char name[BDEVNAME_SIZE];
|
||||
|
||||
@ -990,11 +993,6 @@ struct bch_fs {
|
||||
struct bio_set dio_read_bioset;
|
||||
struct bio_set nocow_flush_bioset;
|
||||
|
||||
/* ERRORS */
|
||||
struct list_head fsck_errors;
|
||||
struct mutex fsck_error_lock;
|
||||
bool fsck_alloc_err;
|
||||
|
||||
/* QUOTAS */
|
||||
struct bch_memquota_type quotas[QTYP_NR];
|
||||
|
||||
@ -1043,6 +1041,14 @@ struct bch_fs {
|
||||
struct bch2_time_stats times[BCH_TIME_STAT_NR];
|
||||
|
||||
struct btree_transaction_stats btree_transaction_stats[BCH_TRANSACTIONS_NR];
|
||||
|
||||
/* ERRORS */
|
||||
struct list_head fsck_error_msgs;
|
||||
struct mutex fsck_error_msgs_lock;
|
||||
bool fsck_alloc_msgs_err;
|
||||
|
||||
bch_sb_errors_cpu fsck_error_counts;
|
||||
struct mutex fsck_error_counts_lock;
|
||||
};
|
||||
|
||||
extern struct wait_queue_head bch2_read_only_wait;
|
||||
|
@ -1218,7 +1218,8 @@ struct bch_sb_field {
|
||||
x(journal_seq_blacklist, 8) \
|
||||
x(journal_v2, 9) \
|
||||
x(counters, 10) \
|
||||
x(members_v2, 11)
|
||||
x(members_v2, 11) \
|
||||
x(errors, 12)
|
||||
|
||||
enum bch_sb_field_type {
|
||||
#define x(f, nr) BCH_SB_FIELD_##f = nr,
|
||||
@ -1268,6 +1269,18 @@ enum bch_iops_measurement {
|
||||
BCH_IOPS_NR
|
||||
};
|
||||
|
||||
#define BCH_MEMBER_ERROR_TYPES() \
|
||||
x(read, 0) \
|
||||
x(write, 1) \
|
||||
x(checksum, 2)
|
||||
|
||||
enum bch_member_error_type {
|
||||
#define x(t, n) BCH_MEMBER_ERROR_##t = n,
|
||||
BCH_MEMBER_ERROR_TYPES()
|
||||
#undef x
|
||||
BCH_MEMBER_ERROR_NR
|
||||
};
|
||||
|
||||
struct bch_member {
|
||||
__uuid_t uuid;
|
||||
__le64 nbuckets; /* device size */
|
||||
@ -1278,6 +1291,9 @@ struct bch_member {
|
||||
|
||||
__le64 flags;
|
||||
__le32 iops[4];
|
||||
__le64 errors[BCH_MEMBER_ERROR_NR];
|
||||
__le64 errors_at_reset[BCH_MEMBER_ERROR_NR];
|
||||
__le64 errors_reset_time;
|
||||
};
|
||||
|
||||
#define BCH_MEMBER_V1_BYTES 56
|
||||
@ -1606,6 +1622,17 @@ struct bch_sb_field_journal_seq_blacklist {
|
||||
__u64 _data[];
|
||||
};
|
||||
|
||||
struct bch_sb_field_errors {
|
||||
struct bch_sb_field field;
|
||||
struct bch_sb_field_error_entry {
|
||||
__le64 v;
|
||||
__le64 last_error_time;
|
||||
} entries[];
|
||||
};
|
||||
|
||||
LE64_BITMASK(BCH_SB_ERROR_ENTRY_ID, struct bch_sb_field_error_entry, v, 0, 16);
|
||||
LE64_BITMASK(BCH_SB_ERROR_ENTRY_NR, struct bch_sb_field_error_entry, v, 16, 64);
|
||||
|
||||
/* Superblock: */
|
||||
|
||||
/*
|
||||
|
@ -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_val_bytes(k.k), ops->min_val_size);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
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);
|
||||
|
||||
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)",
|
||||
bch2_btree_node_type_str(type), bch2_bkey_types[k.k->type]);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
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]);
|
||||
|
||||
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)",
|
||||
k.k->size, k.k->p.offset);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
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);
|
||||
} 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)
|
||||
|
@ -21,7 +21,7 @@ extern const struct bkey_ops bch2_bkey_null_ops;
|
||||
* being read or written; more aggressive checks can be enabled when rw == WRITE.
|
||||
*/
|
||||
struct bkey_ops {
|
||||
int (*key_invalid)(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int (*key_invalid)(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags, struct printbuf *err);
|
||||
void (*val_to_text)(struct printbuf *, struct bch_fs *,
|
||||
struct bkey_s_c);
|
||||
@ -55,7 +55,8 @@ int __bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c, enum btree_node_type,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
int bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c, enum btree_node_type,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
int bch2_bkey_in_btree_node(struct btree *, struct bkey_s_c, struct printbuf *);
|
||||
int bch2_bkey_in_btree_node(struct bch_fs *, struct btree *,
|
||||
struct bkey_s_c, struct printbuf *);
|
||||
|
||||
void bch2_bpos_to_text(struct printbuf *, struct bpos);
|
||||
void bch2_bkey_to_text(struct printbuf *, const struct bkey *);
|
||||
|
@ -95,15 +95,15 @@ static int bch2_gc_check_topology(struct bch_fs *c,
|
||||
bch2_bkey_val_to_text(&buf2, c, bkey_i_to_s_c(cur.k));
|
||||
|
||||
if (__fsck_err(c,
|
||||
FSCK_CAN_FIX|
|
||||
FSCK_CAN_IGNORE|
|
||||
FSCK_NO_RATELIMIT,
|
||||
"btree node with incorrect min_key at btree %s level %u:\n"
|
||||
" prev %s\n"
|
||||
" cur %s",
|
||||
bch2_btree_id_str(b->c.btree_id), b->c.level,
|
||||
buf1.buf, buf2.buf) &&
|
||||
should_restart_for_topology_repair(c)) {
|
||||
FSCK_CAN_FIX|
|
||||
FSCK_CAN_IGNORE|
|
||||
FSCK_NO_RATELIMIT,
|
||||
btree_node_topology_bad_min_key,
|
||||
"btree node with incorrect min_key at btree %s level %u:\n"
|
||||
" prev %s\n"
|
||||
" cur %s",
|
||||
bch2_btree_id_str(b->c.btree_id), b->c.level,
|
||||
buf1.buf, buf2.buf) && should_restart_for_topology_repair(c)) {
|
||||
bch_info(c, "Halting mark and sweep to start topology repair pass");
|
||||
ret = bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_topology);
|
||||
goto err;
|
||||
@ -122,10 +122,8 @@ static int bch2_gc_check_topology(struct bch_fs *c,
|
||||
bch2_bkey_val_to_text(&buf1, c, bkey_i_to_s_c(cur.k));
|
||||
bch2_bpos_to_text(&buf2, node_end);
|
||||
|
||||
if (__fsck_err(c,
|
||||
FSCK_CAN_FIX|
|
||||
FSCK_CAN_IGNORE|
|
||||
FSCK_NO_RATELIMIT,
|
||||
if (__fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE|FSCK_NO_RATELIMIT,
|
||||
btree_node_topology_bad_max_key,
|
||||
"btree node with incorrect max_key at btree %s level %u:\n"
|
||||
" %s\n"
|
||||
" expected %s",
|
||||
@ -287,6 +285,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
|
||||
|
||||
if (mustfix_fsck_err_on(bpos_ge(prev->data->min_key,
|
||||
cur->data->min_key), c,
|
||||
btree_node_topology_overwritten_by_next_node,
|
||||
"btree node overwritten by next node at btree %s level %u:\n"
|
||||
" node %s\n"
|
||||
" next %s",
|
||||
@ -298,6 +297,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
|
||||
|
||||
if (mustfix_fsck_err_on(!bpos_eq(prev->key.k.p,
|
||||
bpos_predecessor(cur->data->min_key)), c,
|
||||
btree_node_topology_bad_max_key,
|
||||
"btree node with incorrect max_key at btree %s level %u:\n"
|
||||
" node %s\n"
|
||||
" next %s",
|
||||
@ -310,6 +310,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
|
||||
|
||||
if (mustfix_fsck_err_on(bpos_ge(expected_start,
|
||||
cur->data->max_key), c,
|
||||
btree_node_topology_overwritten_by_prev_node,
|
||||
"btree node overwritten by prev node at btree %s level %u:\n"
|
||||
" prev %s\n"
|
||||
" node %s",
|
||||
@ -320,6 +321,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
|
||||
}
|
||||
|
||||
if (mustfix_fsck_err_on(!bpos_eq(expected_start, cur->data->min_key), c,
|
||||
btree_node_topology_bad_min_key,
|
||||
"btree node with incorrect min_key at btree %s level %u:\n"
|
||||
" prev %s\n"
|
||||
" node %s",
|
||||
@ -344,6 +346,7 @@ static int btree_repair_node_end(struct bch_fs *c, struct btree *b,
|
||||
bch2_bpos_to_text(&buf2, b->key.k.p);
|
||||
|
||||
if (mustfix_fsck_err_on(!bpos_eq(child->key.k.p, b->key.k.p), c,
|
||||
btree_node_topology_bad_max_key,
|
||||
"btree node with incorrect max_key at btree %s level %u:\n"
|
||||
" %s\n"
|
||||
" expected %s",
|
||||
@ -396,6 +399,7 @@ again:
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(cur_k.k));
|
||||
|
||||
if (mustfix_fsck_err_on(ret == -EIO, c,
|
||||
btree_node_unreadable,
|
||||
"Topology repair: unreadable btree node at btree %s level %u:\n"
|
||||
" %s",
|
||||
bch2_btree_id_str(b->c.btree_id),
|
||||
@ -504,6 +508,7 @@ again:
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
|
||||
|
||||
if (mustfix_fsck_err_on(!have_child, c,
|
||||
btree_node_topology_interior_node_empty,
|
||||
"empty interior btree node at btree %s level %u\n"
|
||||
" %s",
|
||||
bch2_btree_id_str(b->c.btree_id),
|
||||
@ -582,7 +587,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
|
||||
|
||||
if (!g->gen_valid &&
|
||||
(c->opts.reconstruct_alloc ||
|
||||
fsck_err(c, "bucket %u:%zu data type %s ptr gen %u missing in alloc btree\n"
|
||||
fsck_err(c, ptr_to_missing_alloc_key,
|
||||
"bucket %u:%zu data type %s ptr gen %u missing in alloc btree\n"
|
||||
"while marking %s",
|
||||
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr),
|
||||
bch2_data_types[ptr_data_type(k->k, &p.ptr)],
|
||||
@ -599,7 +605,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
|
||||
|
||||
if (gen_cmp(p.ptr.gen, g->gen) > 0 &&
|
||||
(c->opts.reconstruct_alloc ||
|
||||
fsck_err(c, "bucket %u:%zu data type %s ptr gen in the future: %u > %u\n"
|
||||
fsck_err(c, ptr_gen_newer_than_bucket_gen,
|
||||
"bucket %u:%zu data type %s ptr gen in the future: %u > %u\n"
|
||||
"while marking %s",
|
||||
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr),
|
||||
bch2_data_types[ptr_data_type(k->k, &p.ptr)],
|
||||
@ -620,7 +627,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
|
||||
|
||||
if (gen_cmp(g->gen, p.ptr.gen) > BUCKET_GC_GEN_MAX &&
|
||||
(c->opts.reconstruct_alloc ||
|
||||
fsck_err(c, "bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n"
|
||||
fsck_err(c, ptr_gen_newer_than_bucket_gen,
|
||||
"bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n"
|
||||
"while marking %s",
|
||||
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), g->gen,
|
||||
bch2_data_types[ptr_data_type(k->k, &p.ptr)],
|
||||
@ -631,7 +639,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
|
||||
|
||||
if (!p.ptr.cached && gen_cmp(p.ptr.gen, g->gen) < 0 &&
|
||||
(c->opts.reconstruct_alloc ||
|
||||
fsck_err(c, "bucket %u:%zu data type %s stale dirty ptr: %u < %u\n"
|
||||
fsck_err(c, stale_dirty_ptr,
|
||||
"bucket %u:%zu data type %s stale dirty ptr: %u < %u\n"
|
||||
"while marking %s",
|
||||
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr),
|
||||
bch2_data_types[ptr_data_type(k->k, &p.ptr)],
|
||||
@ -645,6 +654,7 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
|
||||
|
||||
if (fsck_err_on(bucket_data_type(g->data_type) &&
|
||||
bucket_data_type(g->data_type) != data_type, c,
|
||||
ptr_bucket_data_type_mismatch,
|
||||
"bucket %u:%zu different types of data in same bucket: %s, %s\n"
|
||||
"while marking %s",
|
||||
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr),
|
||||
@ -664,6 +674,7 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
|
||||
struct gc_stripe *m = genradix_ptr(&c->gc_stripes, p.ec.idx);
|
||||
|
||||
if (fsck_err_on(!m || !m->alive, c,
|
||||
ptr_to_missing_stripe,
|
||||
"pointer to nonexistent stripe %llu\n"
|
||||
"while marking %s",
|
||||
(u64) p.ec.idx,
|
||||
@ -672,6 +683,7 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
|
||||
do_update = true;
|
||||
|
||||
if (fsck_err_on(m && m->alive && !bch2_ptr_matches_stripe_m(m, p), c,
|
||||
ptr_to_incorrect_stripe,
|
||||
"pointer does not match stripe %llu\n"
|
||||
"while marking %s",
|
||||
(u64) p.ec.idx,
|
||||
@ -811,6 +823,7 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(k->k->version.lo > atomic64_read(&c->key_version), c,
|
||||
bkey_version_in_future,
|
||||
"key version number higher than recorded: %llu > %llu",
|
||||
k->k->version.lo,
|
||||
atomic64_read(&c->key_version)))
|
||||
@ -968,6 +981,7 @@ static int bch2_gc_btree_init_recurse(struct btree_trans *trans, struct btree *b
|
||||
FSCK_CAN_FIX|
|
||||
FSCK_CAN_IGNORE|
|
||||
FSCK_NO_RATELIMIT,
|
||||
btree_node_read_error,
|
||||
"Unreadable btree node at btree %s level %u:\n"
|
||||
" %s",
|
||||
bch2_btree_id_str(b->c.btree_id),
|
||||
@ -1025,6 +1039,7 @@ static int bch2_gc_btree_init(struct btree_trans *trans,
|
||||
printbuf_reset(&buf);
|
||||
bch2_bpos_to_text(&buf, b->data->min_key);
|
||||
if (mustfix_fsck_err_on(!bpos_eq(b->data->min_key, POS_MIN), c,
|
||||
btree_root_bad_min_key,
|
||||
"btree root with incorrect min_key: %s", buf.buf)) {
|
||||
bch_err(c, "repair unimplemented");
|
||||
ret = -BCH_ERR_fsck_repair_unimplemented;
|
||||
@ -1034,6 +1049,7 @@ static int bch2_gc_btree_init(struct btree_trans *trans,
|
||||
printbuf_reset(&buf);
|
||||
bch2_bpos_to_text(&buf, b->data->max_key);
|
||||
if (mustfix_fsck_err_on(!bpos_eq(b->data->max_key, SPOS_MAX), c,
|
||||
btree_root_bad_max_key,
|
||||
"btree root with incorrect max_key: %s", buf.buf)) {
|
||||
bch_err(c, "repair unimplemented");
|
||||
ret = -BCH_ERR_fsck_repair_unimplemented;
|
||||
@ -1210,16 +1226,16 @@ static int bch2_gc_done(struct bch_fs *c,
|
||||
|
||||
percpu_down_write(&c->mark_lock);
|
||||
|
||||
#define copy_field(_f, _msg, ...) \
|
||||
#define copy_field(_err, _f, _msg, ...) \
|
||||
if (dst->_f != src->_f && \
|
||||
(!verify || \
|
||||
fsck_err(c, _msg ": got %llu, should be %llu" \
|
||||
fsck_err(c, _err, _msg ": got %llu, should be %llu" \
|
||||
, ##__VA_ARGS__, dst->_f, src->_f))) \
|
||||
dst->_f = src->_f
|
||||
#define copy_dev_field(_f, _msg, ...) \
|
||||
copy_field(_f, "dev %u has wrong " _msg, dev, ##__VA_ARGS__)
|
||||
#define copy_fs_field(_f, _msg, ...) \
|
||||
copy_field(_f, "fs has wrong " _msg, ##__VA_ARGS__)
|
||||
#define copy_dev_field(_err, _f, _msg, ...) \
|
||||
copy_field(_err, _f, "dev %u has wrong " _msg, dev, ##__VA_ARGS__)
|
||||
#define copy_fs_field(_err, _f, _msg, ...) \
|
||||
copy_field(_err, _f, "fs has wrong " _msg, ##__VA_ARGS__)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(c->usage); i++)
|
||||
bch2_fs_usage_acc_to_base(c, i);
|
||||
@ -1230,13 +1246,17 @@ static int bch2_gc_done(struct bch_fs *c,
|
||||
bch2_acc_percpu_u64s((u64 __percpu *) ca->usage_gc,
|
||||
dev_usage_u64s());
|
||||
|
||||
copy_dev_field(buckets_ec, "buckets_ec");
|
||||
|
||||
for (i = 0; i < BCH_DATA_NR; i++) {
|
||||
copy_dev_field(d[i].buckets, "%s buckets", bch2_data_types[i]);
|
||||
copy_dev_field(d[i].sectors, "%s sectors", bch2_data_types[i]);
|
||||
copy_dev_field(d[i].fragmented, "%s fragmented", bch2_data_types[i]);
|
||||
copy_dev_field(dev_usage_buckets_wrong,
|
||||
d[i].buckets, "%s buckets", bch2_data_types[i]);
|
||||
copy_dev_field(dev_usage_sectors_wrong,
|
||||
d[i].sectors, "%s sectors", bch2_data_types[i]);
|
||||
copy_dev_field(dev_usage_fragmented_wrong,
|
||||
d[i].fragmented, "%s fragmented", bch2_data_types[i]);
|
||||
}
|
||||
|
||||
copy_dev_field(dev_usage_buckets_ec_wrong,
|
||||
buckets_ec, "buckets_ec");
|
||||
}
|
||||
|
||||
{
|
||||
@ -1245,17 +1265,24 @@ static int bch2_gc_done(struct bch_fs *c,
|
||||
struct bch_fs_usage *src = (void *)
|
||||
bch2_acc_percpu_u64s((u64 __percpu *) c->usage_gc, nr);
|
||||
|
||||
copy_fs_field(hidden, "hidden");
|
||||
copy_fs_field(btree, "btree");
|
||||
copy_fs_field(fs_usage_hidden_wrong,
|
||||
hidden, "hidden");
|
||||
copy_fs_field(fs_usage_btree_wrong,
|
||||
btree, "btree");
|
||||
|
||||
if (!metadata_only) {
|
||||
copy_fs_field(data, "data");
|
||||
copy_fs_field(cached, "cached");
|
||||
copy_fs_field(reserved, "reserved");
|
||||
copy_fs_field(nr_inodes,"nr_inodes");
|
||||
copy_fs_field(fs_usage_data_wrong,
|
||||
data, "data");
|
||||
copy_fs_field(fs_usage_cached_wrong,
|
||||
cached, "cached");
|
||||
copy_fs_field(fs_usage_reserved_wrong,
|
||||
reserved, "reserved");
|
||||
copy_fs_field(fs_usage_nr_inodes_wrong,
|
||||
nr_inodes,"nr_inodes");
|
||||
|
||||
for (i = 0; i < BCH_REPLICAS_MAX; i++)
|
||||
copy_fs_field(persistent_reserved[i],
|
||||
copy_fs_field(fs_usage_persistent_reserved_wrong,
|
||||
persistent_reserved[i],
|
||||
"persistent_reserved[%i]", i);
|
||||
}
|
||||
|
||||
@ -1271,7 +1298,8 @@ static int bch2_gc_done(struct bch_fs *c,
|
||||
printbuf_reset(&buf);
|
||||
bch2_replicas_entry_to_text(&buf, e);
|
||||
|
||||
copy_fs_field(replicas[i], "%s", buf.buf);
|
||||
copy_fs_field(fs_usage_replicas_wrong,
|
||||
replicas[i], "%s", buf.buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1407,6 +1435,7 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
|
||||
|
||||
if (c->opts.reconstruct_alloc ||
|
||||
fsck_err_on(new.data_type != gc.data_type, c,
|
||||
alloc_key_data_type_wrong,
|
||||
"bucket %llu:%llu gen %u has wrong data_type"
|
||||
": got %s, should be %s",
|
||||
iter->pos.inode, iter->pos.offset,
|
||||
@ -1415,9 +1444,9 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
|
||||
bch2_data_types[gc.data_type]))
|
||||
new.data_type = gc.data_type;
|
||||
|
||||
#define copy_bucket_field(_f) \
|
||||
#define copy_bucket_field(_errtype, _f) \
|
||||
if (c->opts.reconstruct_alloc || \
|
||||
fsck_err_on(new._f != gc._f, c, \
|
||||
fsck_err_on(new._f != gc._f, c, _errtype, \
|
||||
"bucket %llu:%llu gen %u data type %s has wrong " #_f \
|
||||
": got %u, should be %u", \
|
||||
iter->pos.inode, iter->pos.offset, \
|
||||
@ -1426,11 +1455,16 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
|
||||
new._f, gc._f)) \
|
||||
new._f = gc._f; \
|
||||
|
||||
copy_bucket_field(gen);
|
||||
copy_bucket_field(dirty_sectors);
|
||||
copy_bucket_field(cached_sectors);
|
||||
copy_bucket_field(stripe_redundancy);
|
||||
copy_bucket_field(stripe);
|
||||
copy_bucket_field(alloc_key_gen_wrong,
|
||||
gen);
|
||||
copy_bucket_field(alloc_key_dirty_sectors_wrong,
|
||||
dirty_sectors);
|
||||
copy_bucket_field(alloc_key_cached_sectors_wrong,
|
||||
cached_sectors);
|
||||
copy_bucket_field(alloc_key_stripe_wrong,
|
||||
stripe);
|
||||
copy_bucket_field(alloc_key_stripe_redundancy_wrong,
|
||||
stripe_redundancy);
|
||||
#undef copy_bucket_field
|
||||
|
||||
if (!bch2_alloc_v4_cmp(*old, new))
|
||||
@ -1587,6 +1621,7 @@ static int bch2_gc_write_reflink_key(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (fsck_err_on(r->refcount != le64_to_cpu(*refcount), c,
|
||||
reflink_v_refcount_wrong,
|
||||
"reflink key has wrong refcount:\n"
|
||||
" %s\n"
|
||||
" should be %u",
|
||||
@ -1712,7 +1747,8 @@ static int bch2_gc_write_stripes_key(struct btree_trans *trans,
|
||||
if (bad)
|
||||
bch2_bkey_val_to_text(&buf, c, k);
|
||||
|
||||
if (fsck_err_on(bad, c, "%s", buf.buf)) {
|
||||
if (fsck_err_on(bad, c, stripe_sector_count_wrong,
|
||||
"%s", buf.buf)) {
|
||||
struct bkey_i_stripe *new;
|
||||
|
||||
new = bch2_trans_kmalloc(trans, bkey_bytes(k.k));
|
||||
|
@ -530,7 +530,7 @@ static void btree_err_msg(struct printbuf *out, struct bch_fs *c,
|
||||
prt_str(out, ": ");
|
||||
}
|
||||
|
||||
__printf(8, 9)
|
||||
__printf(9, 10)
|
||||
static int __btree_err(int ret,
|
||||
struct bch_fs *c,
|
||||
struct bch_dev *ca,
|
||||
@ -538,6 +538,7 @@ static int __btree_err(int ret,
|
||||
struct bset *i,
|
||||
int write,
|
||||
bool have_retry,
|
||||
enum bch_sb_error_id err_type,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct printbuf out = PRINTBUF;
|
||||
@ -562,9 +563,15 @@ static int __btree_err(int ret,
|
||||
if (!have_retry && ret == -BCH_ERR_btree_node_read_err_must_retry)
|
||||
ret = -BCH_ERR_btree_node_read_err_bad_node;
|
||||
|
||||
if (ret != -BCH_ERR_btree_node_read_err_fixable)
|
||||
bch2_sb_error_count(c, err_type);
|
||||
|
||||
switch (ret) {
|
||||
case -BCH_ERR_btree_node_read_err_fixable:
|
||||
mustfix_fsck_err(c, "%s", out.buf);
|
||||
ret = bch2_fsck_err(c, FSCK_CAN_FIX, err_type, "%s", out.buf);
|
||||
if (ret != -BCH_ERR_fsck_fix &&
|
||||
ret != -BCH_ERR_fsck_ignore)
|
||||
goto fsck_err;
|
||||
ret = -BCH_ERR_fsck_fix;
|
||||
break;
|
||||
case -BCH_ERR_btree_node_read_err_want_retry:
|
||||
@ -589,9 +596,11 @@ fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define btree_err(type, c, ca, b, i, msg, ...) \
|
||||
#define btree_err(type, c, ca, b, i, _err_type, msg, ...) \
|
||||
({ \
|
||||
int _ret = __btree_err(type, c, ca, b, i, write, have_retry, msg, ##__VA_ARGS__);\
|
||||
int _ret = __btree_err(type, c, ca, b, i, write, have_retry, \
|
||||
BCH_FSCK_ERR_##_err_type, \
|
||||
msg, ##__VA_ARGS__); \
|
||||
\
|
||||
if (_ret != -BCH_ERR_fsck_fix) { \
|
||||
ret = _ret; \
|
||||
@ -666,13 +675,17 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
int ret = 0;
|
||||
|
||||
btree_err_on(!bch2_version_compatible(version),
|
||||
-BCH_ERR_btree_node_read_err_incompatible, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_incompatible,
|
||||
c, ca, b, i,
|
||||
btree_node_unsupported_version,
|
||||
"unsupported bset version %u.%u",
|
||||
BCH_VERSION_MAJOR(version),
|
||||
BCH_VERSION_MINOR(version));
|
||||
|
||||
if (btree_err_on(version < c->sb.version_min,
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bset_older_than_sb_min,
|
||||
"bset version %u older than superblock version_min %u",
|
||||
version, c->sb.version_min)) {
|
||||
mutex_lock(&c->sb_lock);
|
||||
@ -683,7 +696,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
|
||||
if (btree_err_on(BCH_VERSION_MAJOR(version) >
|
||||
BCH_VERSION_MAJOR(c->sb.version),
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bset_newer_than_sb,
|
||||
"bset version %u newer than superblock version %u",
|
||||
version, c->sb.version)) {
|
||||
mutex_lock(&c->sb_lock);
|
||||
@ -693,11 +708,15 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
}
|
||||
|
||||
btree_err_on(BSET_SEPARATE_WHITEOUTS(i),
|
||||
-BCH_ERR_btree_node_read_err_incompatible, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_incompatible,
|
||||
c, ca, b, i,
|
||||
btree_node_unsupported_version,
|
||||
"BSET_SEPARATE_WHITEOUTS no longer supported");
|
||||
|
||||
if (btree_err_on(offset + sectors > btree_sectors(c),
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, ca, b, i,
|
||||
bset_past_end_of_btree_node,
|
||||
"bset past end of btree node")) {
|
||||
i->u64s = 0;
|
||||
ret = 0;
|
||||
@ -705,12 +724,15 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
}
|
||||
|
||||
btree_err_on(offset && !i->u64s,
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, ca, b, i,
|
||||
bset_empty,
|
||||
"empty bset");
|
||||
|
||||
btree_err_on(BSET_OFFSET(i) &&
|
||||
BSET_OFFSET(i) != offset,
|
||||
-BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i,
|
||||
btree_err_on(BSET_OFFSET(i) && BSET_OFFSET(i) != offset,
|
||||
-BCH_ERR_btree_node_read_err_want_retry,
|
||||
c, ca, b, i,
|
||||
bset_wrong_sector_offset,
|
||||
"bset at wrong sector offset");
|
||||
|
||||
if (!offset) {
|
||||
@ -724,16 +746,22 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
|
||||
/* XXX endianness */
|
||||
btree_err_on(bp->seq != bn->keys.seq,
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, NULL,
|
||||
bset_bad_seq,
|
||||
"incorrect sequence number (wrong btree node)");
|
||||
}
|
||||
|
||||
btree_err_on(BTREE_NODE_ID(bn) != b->c.btree_id,
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, i,
|
||||
btree_node_bad_btree,
|
||||
"incorrect btree id");
|
||||
|
||||
btree_err_on(BTREE_NODE_LEVEL(bn) != b->c.level,
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, i,
|
||||
btree_node_bad_level,
|
||||
"incorrect level");
|
||||
|
||||
if (!write)
|
||||
@ -750,7 +778,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
}
|
||||
|
||||
btree_err_on(!bpos_eq(b->data->min_key, bp->min_key),
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, NULL,
|
||||
btree_node_bad_min_key,
|
||||
"incorrect min_key: got %s should be %s",
|
||||
(printbuf_reset(&buf1),
|
||||
bch2_bpos_to_text(&buf1, bn->min_key), buf1.buf),
|
||||
@ -759,7 +789,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
}
|
||||
|
||||
btree_err_on(!bpos_eq(bn->max_key, b->key.k.p),
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, i,
|
||||
btree_node_bad_max_key,
|
||||
"incorrect max key %s",
|
||||
(printbuf_reset(&buf1),
|
||||
bch2_bpos_to_text(&buf1, bn->max_key), buf1.buf));
|
||||
@ -769,7 +801,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
BSET_BIG_ENDIAN(i), write, bn);
|
||||
|
||||
btree_err_on(bch2_bkey_format_invalid(c, &bn->format, write, &buf1),
|
||||
-BCH_ERR_btree_node_read_err_bad_node, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_bad_node,
|
||||
c, ca, b, i,
|
||||
btree_node_bad_format,
|
||||
"invalid bkey format: %s\n %s", buf1.buf,
|
||||
(printbuf_reset(&buf2),
|
||||
bch2_bkey_format_to_text(&buf2, &bn->format), buf2.buf));
|
||||
@ -792,7 +826,7 @@ static int bset_key_invalid(struct bch_fs *c, struct btree *b,
|
||||
struct printbuf *err)
|
||||
{
|
||||
return __bch2_bkey_invalid(c, k, btree_node_type(b), READ, err) ?:
|
||||
(!updated_range ? bch2_bkey_in_btree_node(b, k, err) : 0) ?:
|
||||
(!updated_range ? bch2_bkey_in_btree_node(c, b, k, err) : 0) ?:
|
||||
(rw == WRITE ? bch2_bkey_val_invalid(c, k, READ, err) : 0);
|
||||
}
|
||||
|
||||
@ -813,14 +847,18 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
|
||||
struct bkey tmp;
|
||||
|
||||
if (btree_err_on(bkey_p_next(k) > vstruct_last(i),
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bkey_past_bset_end,
|
||||
"key extends past end of bset")) {
|
||||
i->u64s = cpu_to_le16((u64 *) k - i->_data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (btree_err_on(k->format > KEY_FORMAT_CURRENT,
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bkey_bad_format,
|
||||
"invalid bkey format %u", k->format)) {
|
||||
i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
|
||||
memmove_u64s_down(k, bkey_p_next(k),
|
||||
@ -839,12 +877,14 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
|
||||
printbuf_reset(&buf);
|
||||
if (bset_key_invalid(c, b, u.s_c, updated_range, write, &buf)) {
|
||||
printbuf_reset(&buf);
|
||||
prt_printf(&buf, "invalid bkey: ");
|
||||
bset_key_invalid(c, b, u.s_c, updated_range, write, &buf);
|
||||
prt_printf(&buf, "\n ");
|
||||
bch2_bkey_val_to_text(&buf, c, u.s_c);
|
||||
|
||||
btree_err(-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, "%s", buf.buf);
|
||||
btree_err(-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bad_bkey,
|
||||
"invalid bkey: %s", buf.buf);
|
||||
|
||||
i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
|
||||
memmove_u64s_down(k, bkey_p_next(k),
|
||||
@ -868,7 +908,10 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
|
||||
|
||||
bch2_dump_bset(c, b, i, 0);
|
||||
|
||||
if (btree_err(-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, "%s", buf.buf)) {
|
||||
if (btree_err(-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bkey_out_of_order,
|
||||
"%s", buf.buf)) {
|
||||
i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
|
||||
memmove_u64s_down(k, bkey_p_next(k),
|
||||
(u64 *) vstruct_end(i) - (u64 *) k);
|
||||
@ -909,47 +952,62 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
sort_iter_init(iter, b, (btree_blocks(c) + 1) * 2);
|
||||
|
||||
if (bch2_meta_read_fault("btree"))
|
||||
btree_err(-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL,
|
||||
btree_err(-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, NULL,
|
||||
btree_node_fault_injected,
|
||||
"dynamic fault");
|
||||
|
||||
btree_err_on(le64_to_cpu(b->data->magic) != bset_magic(c),
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, NULL,
|
||||
btree_node_bad_magic,
|
||||
"bad magic: want %llx, got %llx",
|
||||
bset_magic(c), le64_to_cpu(b->data->magic));
|
||||
|
||||
btree_err_on(!b->data->keys.seq,
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL,
|
||||
"bad btree header: seq 0");
|
||||
|
||||
if (b->key.k.type == KEY_TYPE_btree_ptr_v2) {
|
||||
struct bch_btree_ptr_v2 *bp =
|
||||
&bkey_i_to_btree_ptr_v2(&b->key)->v;
|
||||
|
||||
btree_err_on(b->data->keys.seq != bp->seq,
|
||||
-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, NULL,
|
||||
btree_node_bad_seq,
|
||||
"got wrong btree node (seq %llx want %llx)",
|
||||
b->data->keys.seq, bp->seq);
|
||||
} else {
|
||||
btree_err_on(!b->data->keys.seq,
|
||||
-BCH_ERR_btree_node_read_err_must_retry,
|
||||
c, ca, b, NULL,
|
||||
btree_node_bad_seq,
|
||||
"bad btree header: seq 0");
|
||||
}
|
||||
|
||||
while (b->written < (ptr_written ?: btree_sectors(c))) {
|
||||
unsigned sectors;
|
||||
struct nonce nonce;
|
||||
struct bch_csum csum;
|
||||
bool first = !b->written;
|
||||
bool csum_bad;
|
||||
|
||||
if (!b->written) {
|
||||
i = &b->data->keys;
|
||||
|
||||
btree_err_on(!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)),
|
||||
-BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i,
|
||||
"unknown checksum type %llu",
|
||||
BSET_CSUM_TYPE(i));
|
||||
-BCH_ERR_btree_node_read_err_want_retry,
|
||||
c, ca, b, i,
|
||||
bset_unknown_csum,
|
||||
"unknown checksum type %llu", BSET_CSUM_TYPE(i));
|
||||
|
||||
nonce = btree_nonce(i, b->written << 9);
|
||||
csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, b->data);
|
||||
|
||||
btree_err_on(bch2_crc_cmp(csum, b->data->csum),
|
||||
-BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i,
|
||||
csum_bad = bch2_crc_cmp(b->data->csum,
|
||||
csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, b->data));
|
||||
if (csum_bad)
|
||||
bch2_io_error(ca, BCH_MEMBER_ERROR_checksum);
|
||||
|
||||
btree_err_on(csum_bad,
|
||||
-BCH_ERR_btree_node_read_err_want_retry,
|
||||
c, ca, b, i,
|
||||
bset_bad_csum,
|
||||
"invalid checksum");
|
||||
|
||||
ret = bset_encrypt(c, i, b->written << 9);
|
||||
@ -959,7 +1017,9 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
|
||||
btree_err_on(btree_node_type_is_extents(btree_node_type(b)) &&
|
||||
!BTREE_NODE_NEW_EXTENT_OVERWRITE(b->data),
|
||||
-BCH_ERR_btree_node_read_err_incompatible, c, NULL, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_incompatible,
|
||||
c, NULL, b, NULL,
|
||||
btree_node_unsupported_version,
|
||||
"btree node does not have NEW_EXTENT_OVERWRITE set");
|
||||
|
||||
sectors = vstruct_sectors(b->data, c->block_bits);
|
||||
@ -971,15 +1031,21 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
break;
|
||||
|
||||
btree_err_on(!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)),
|
||||
-BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i,
|
||||
"unknown checksum type %llu",
|
||||
BSET_CSUM_TYPE(i));
|
||||
-BCH_ERR_btree_node_read_err_want_retry,
|
||||
c, ca, b, i,
|
||||
bset_unknown_csum,
|
||||
"unknown checksum type %llu", BSET_CSUM_TYPE(i));
|
||||
|
||||
nonce = btree_nonce(i, b->written << 9);
|
||||
csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne);
|
||||
csum_bad = bch2_crc_cmp(bne->csum,
|
||||
csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne));
|
||||
if (csum_bad)
|
||||
bch2_io_error(ca, BCH_MEMBER_ERROR_checksum);
|
||||
|
||||
btree_err_on(bch2_crc_cmp(csum, bne->csum),
|
||||
-BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i,
|
||||
btree_err_on(csum_bad,
|
||||
-BCH_ERR_btree_node_read_err_want_retry,
|
||||
c, ca, b, i,
|
||||
bset_bad_csum,
|
||||
"invalid checksum");
|
||||
|
||||
ret = bset_encrypt(c, i, b->written << 9);
|
||||
@ -1012,12 +1078,16 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
true);
|
||||
|
||||
btree_err_on(blacklisted && first,
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, ca, b, i,
|
||||
bset_blacklisted_journal_seq,
|
||||
"first btree node bset has blacklisted journal seq (%llu)",
|
||||
le64_to_cpu(i->journal_seq));
|
||||
|
||||
btree_err_on(blacklisted && ptr_written,
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, ca, b, i,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, ca, b, i,
|
||||
first_bset_blacklisted_journal_seq,
|
||||
"found blacklisted bset (journal seq %llu) in btree node at offset %u-%u/%u",
|
||||
le64_to_cpu(i->journal_seq),
|
||||
b->written, b->written + sectors, ptr_written);
|
||||
@ -1034,7 +1104,9 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
|
||||
if (ptr_written) {
|
||||
btree_err_on(b->written < ptr_written,
|
||||
-BCH_ERR_btree_node_read_err_want_retry, c, ca, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_want_retry,
|
||||
c, ca, b, NULL,
|
||||
btree_node_data_missing,
|
||||
"btree node data missing: expected %u sectors, found %u",
|
||||
ptr_written, b->written);
|
||||
} else {
|
||||
@ -1045,7 +1117,9 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
!bch2_journal_seq_is_blacklisted(c,
|
||||
le64_to_cpu(bne->keys.journal_seq),
|
||||
true),
|
||||
-BCH_ERR_btree_node_read_err_want_retry, c, ca, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_want_retry,
|
||||
c, ca, b, NULL,
|
||||
btree_node_bset_after_end,
|
||||
"found bset signature after last bset");
|
||||
}
|
||||
|
||||
@ -1087,7 +1161,10 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
prt_printf(&buf, "\n ");
|
||||
bch2_bkey_val_to_text(&buf, c, u.s_c);
|
||||
|
||||
btree_err(-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, "%s", buf.buf);
|
||||
btree_err(-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bad_bkey,
|
||||
"%s", buf.buf);
|
||||
|
||||
btree_keys_account_key_drop(&b->nr, 0, k);
|
||||
|
||||
@ -1168,7 +1245,8 @@ static void btree_node_read_work(struct work_struct *work)
|
||||
start:
|
||||
printbuf_reset(&buf);
|
||||
bch2_btree_pos_to_text(&buf, c, b);
|
||||
bch2_dev_io_err_on(bio->bi_status, ca, "btree read error %s for %s",
|
||||
bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_read,
|
||||
"btree read error %s for %s",
|
||||
bch2_blk_status_to_str(bio->bi_status), buf.buf);
|
||||
if (rb->have_ioref)
|
||||
percpu_ref_put(&ca->io_ref);
|
||||
@ -1312,14 +1390,20 @@ static void btree_node_read_all_replicas_done(struct closure *cl)
|
||||
}
|
||||
|
||||
written2 = btree_node_sectors_written(c, ra->buf[i]);
|
||||
if (btree_err_on(written2 != written, -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, NULL,
|
||||
if (btree_err_on(written2 != written, -BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, NULL,
|
||||
btree_node_replicas_sectors_written_mismatch,
|
||||
"btree node sectors written mismatch: %u != %u",
|
||||
written, written2) ||
|
||||
btree_err_on(btree_node_has_extra_bsets(c, written2, ra->buf[i]),
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, NULL,
|
||||
btree_node_bset_after_end,
|
||||
"found bset signature after last bset") ||
|
||||
btree_err_on(memcmp(ra->buf[best], ra->buf[i], written << 9),
|
||||
-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, NULL,
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, NULL,
|
||||
btree_node_replicas_data_mismatch,
|
||||
"btree node replicas content mismatch"))
|
||||
dump_bset_maps = true;
|
||||
|
||||
@ -1749,7 +1833,8 @@ static void btree_node_write_endio(struct bio *bio)
|
||||
if (wbio->have_ioref)
|
||||
bch2_latency_acct(ca, wbio->submit_time, WRITE);
|
||||
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca, "btree write error: %s",
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write,
|
||||
"btree write error: %s",
|
||||
bch2_blk_status_to_str(bio->bi_status)) ||
|
||||
bch2_meta_write_fault("btree")) {
|
||||
spin_lock_irqsave(&c->btree_write_error_lock, flags);
|
||||
|
@ -1274,14 +1274,14 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as,
|
||||
|
||||
if (bch2_bkey_invalid(c, bkey_i_to_s_c(insert),
|
||||
btree_node_type(b), WRITE, &buf) ?:
|
||||
bch2_bkey_in_btree_node(b, bkey_i_to_s_c(insert), &buf)) {
|
||||
bch2_bkey_in_btree_node(c, b, bkey_i_to_s_c(insert), &buf)) {
|
||||
printbuf_reset(&buf);
|
||||
prt_printf(&buf, "inserting invalid bkey\n ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(insert));
|
||||
prt_printf(&buf, "\n ");
|
||||
bch2_bkey_invalid(c, bkey_i_to_s_c(insert),
|
||||
btree_node_type(b), WRITE, &buf);
|
||||
bch2_bkey_in_btree_node(b, bkey_i_to_s_c(insert), &buf);
|
||||
bch2_bkey_in_btree_node(c, b, bkey_i_to_s_c(insert), &buf);
|
||||
|
||||
bch2_fs_inconsistent(c, "%s", buf.buf);
|
||||
dump_stack();
|
||||
|
@ -370,8 +370,8 @@ static inline int update_replicas(struct bch_fs *c, struct bkey_s_c k,
|
||||
|
||||
idx = bch2_replicas_entry_idx(c, r);
|
||||
if (idx < 0 &&
|
||||
fsck_err(c, "no replicas entry\n"
|
||||
" while marking %s",
|
||||
fsck_err(c, ptr_to_missing_replicas_entry,
|
||||
"no replicas entry\n while marking %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
percpu_up_read(&c->mark_lock);
|
||||
ret = bch2_mark_replicas(c, r);
|
||||
@ -695,6 +695,7 @@ static int check_bucket_ref(struct btree_trans *trans,
|
||||
|
||||
if (gen_after(ptr->gen, b_gen)) {
|
||||
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
||||
BCH_FSCK_ERR_ptr_gen_newer_than_bucket_gen,
|
||||
"bucket %u:%zu gen %u data type %s: ptr gen %u newer than bucket gen\n"
|
||||
"while marking %s",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
@ -707,6 +708,7 @@ static int check_bucket_ref(struct btree_trans *trans,
|
||||
|
||||
if (gen_cmp(b_gen, ptr->gen) > BUCKET_GC_GEN_MAX) {
|
||||
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
||||
BCH_FSCK_ERR_ptr_too_stale,
|
||||
"bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n"
|
||||
"while marking %s",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
@ -720,6 +722,7 @@ static int check_bucket_ref(struct btree_trans *trans,
|
||||
|
||||
if (b_gen != ptr->gen && !ptr->cached) {
|
||||
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
||||
BCH_FSCK_ERR_stale_dirty_ptr,
|
||||
"bucket %u:%zu gen %u (mem gen %u) data type %s: stale dirty ptr (gen %u)\n"
|
||||
"while marking %s",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
@ -741,6 +744,7 @@ static int check_bucket_ref(struct btree_trans *trans,
|
||||
ptr_data_type &&
|
||||
bucket_data_type != ptr_data_type) {
|
||||
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
||||
BCH_FSCK_ERR_ptr_bucket_data_type_mismatch,
|
||||
"bucket %u:%zu gen %u different types of data in same bucket: %s, %s\n"
|
||||
"while marking %s",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
@ -754,6 +758,7 @@ static int check_bucket_ref(struct btree_trans *trans,
|
||||
|
||||
if ((u64) bucket_sectors + sectors > U32_MAX) {
|
||||
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
||||
BCH_FSCK_ERR_bucket_sector_count_overflow,
|
||||
"bucket %u:%zu gen %u data type %s sector count overflow: %u + %lli > U32_MAX\n"
|
||||
"while marking %s",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
@ -1195,7 +1200,8 @@ static s64 __bch2_mark_reflink_p(struct btree_trans *trans,
|
||||
*idx = r->offset;
|
||||
return 0;
|
||||
not_found:
|
||||
if (fsck_err(c, "pointer to missing indirect extent\n"
|
||||
if (fsck_err(c, reflink_p_to_missing_reflink_v,
|
||||
"pointer to missing indirect extent\n"
|
||||
" %s\n"
|
||||
" missing range %llu-%llu",
|
||||
(bch2_bkey_val_to_text(&buf, c, p.s_c), buf.buf),
|
||||
@ -1857,6 +1863,7 @@ static int __bch2_trans_mark_metadata_bucket(struct btree_trans *trans,
|
||||
|
||||
if (a->v.data_type && type && a->v.data_type != type) {
|
||||
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
||||
BCH_FSCK_ERR_bucket_metadata_type_mismatch,
|
||||
"bucket %llu:%llu gen %u different types of data in same bucket: %s, %s\n"
|
||||
"while marking %s",
|
||||
iter.pos.inode, iter.pos.offset, a->v.gen,
|
||||
|
@ -97,61 +97,51 @@ const struct bch_hash_desc bch2_dirent_hash_desc = {
|
||||
.is_visible = dirent_is_visible,
|
||||
};
|
||||
|
||||
int bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_dirent_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
|
||||
struct qstr d_name = bch2_dirent_get_name(d);
|
||||
int ret = 0;
|
||||
|
||||
if (!d_name.len) {
|
||||
prt_printf(err, "empty name");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(!d_name.len, c, err,
|
||||
dirent_empty_name,
|
||||
"empty name");
|
||||
|
||||
if (bkey_val_u64s(k.k) > dirent_val_u64s(d_name.len)) {
|
||||
prt_printf(err, "value too big (%zu > %u)",
|
||||
bkey_val_u64s(k.k), dirent_val_u64s(d_name.len));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bkey_val_u64s(k.k) > dirent_val_u64s(d_name.len), c, err,
|
||||
dirent_val_too_big,
|
||||
"value too big (%zu > %u)",
|
||||
bkey_val_u64s(k.k), dirent_val_u64s(d_name.len));
|
||||
|
||||
/*
|
||||
* Check new keys don't exceed the max length
|
||||
* (older keys may be larger.)
|
||||
*/
|
||||
if ((flags & BKEY_INVALID_COMMIT) && d_name.len > BCH_NAME_MAX) {
|
||||
prt_printf(err, "dirent name too big (%u > %u)",
|
||||
d_name.len, BCH_NAME_MAX);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on((flags & BKEY_INVALID_COMMIT) && d_name.len > BCH_NAME_MAX, c, err,
|
||||
dirent_name_too_long,
|
||||
"dirent name too big (%u > %u)",
|
||||
d_name.len, BCH_NAME_MAX);
|
||||
|
||||
if (d_name.len != strnlen(d_name.name, d_name.len)) {
|
||||
prt_printf(err, "dirent has stray data after name's NUL");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(d_name.len != strnlen(d_name.name, d_name.len), c, err,
|
||||
dirent_name_embedded_nul,
|
||||
"dirent has stray data after name's NUL");
|
||||
|
||||
if (d_name.len == 1 && !memcmp(d_name.name, ".", 1)) {
|
||||
prt_printf(err, "invalid name");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on((d_name.len == 1 && !memcmp(d_name.name, ".", 1)) ||
|
||||
(d_name.len == 2 && !memcmp(d_name.name, "..", 2)), c, err,
|
||||
dirent_name_dot_or_dotdot,
|
||||
"invalid name");
|
||||
|
||||
if (d_name.len == 2 && !memcmp(d_name.name, "..", 2)) {
|
||||
prt_printf(err, "invalid name");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(memchr(d_name.name, '/', d_name.len), c, err,
|
||||
dirent_name_has_slash,
|
||||
"name with /");
|
||||
|
||||
if (memchr(d_name.name, '/', d_name.len)) {
|
||||
prt_printf(err, "invalid name");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
if (d.v->d_type != DT_SUBVOL &&
|
||||
le64_to_cpu(d.v->d_inum) == d.k->p.inode) {
|
||||
prt_printf(err, "dirent points to own directory");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(d.v->d_type != DT_SUBVOL &&
|
||||
le64_to_cpu(d.v->d_inum) == d.k->p.inode, c, err,
|
||||
dirent_to_itself,
|
||||
"dirent points to own directory");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
|
@ -7,7 +7,7 @@
|
||||
enum bkey_invalid_flags;
|
||||
extern const struct bch_hash_desc bch2_dirent_hash_desc;
|
||||
|
||||
int bch2_dirent_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_dirent_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_dirent_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
|
@ -105,29 +105,26 @@ struct ec_bio {
|
||||
|
||||
/* Stripes btree keys: */
|
||||
|
||||
int bch2_stripe_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_stripe_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
const struct bch_stripe *s = bkey_s_c_to_stripe(k).v;
|
||||
int ret = 0;
|
||||
|
||||
if (bkey_eq(k.k->p, POS_MIN)) {
|
||||
prt_printf(err, "stripe at POS_MIN");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bkey_eq(k.k->p, POS_MIN) ||
|
||||
bpos_gt(k.k->p, POS(0, U32_MAX)), c, err,
|
||||
stripe_pos_bad,
|
||||
"stripe at bad pos");
|
||||
|
||||
if (k.k->p.inode) {
|
||||
prt_printf(err, "nonzero inode field");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bkey_val_u64s(k.k) < stripe_val_u64s(s), c, err,
|
||||
stripe_val_size_bad,
|
||||
"incorrect value size (%zu < %u)",
|
||||
bkey_val_u64s(k.k), stripe_val_u64s(s));
|
||||
|
||||
if (bkey_val_u64s(k.k) < stripe_val_u64s(s)) {
|
||||
prt_printf(err, "incorrect value size (%zu < %u)",
|
||||
bkey_val_u64s(k.k), stripe_val_u64s(s));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
return bch2_bkey_ptrs_invalid(c, k, flags, err);
|
||||
ret = bch2_bkey_ptrs_invalid(c, k, flags, err);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_stripe_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
@ -373,7 +370,11 @@ static void ec_block_endio(struct bio *bio)
|
||||
struct bch_dev *ca = ec_bio->ca;
|
||||
struct closure *cl = bio->bi_private;
|
||||
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca, "erasure coding %s error: %s",
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca,
|
||||
bio_data_dir(bio)
|
||||
? BCH_MEMBER_ERROR_write
|
||||
: BCH_MEMBER_ERROR_read,
|
||||
"erasure coding %s error: %s",
|
||||
bio_data_dir(bio) ? "write" : "read",
|
||||
bch2_blk_status_to_str(bio->bi_status)))
|
||||
clear_bit(ec_bio->idx, ec_bio->buf->valid);
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
enum bkey_invalid_flags;
|
||||
|
||||
int bch2_stripe_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_stripe_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_stripe_to_text(struct printbuf *, struct bch_fs *,
|
||||
struct bkey_s_c);
|
||||
|
@ -213,6 +213,7 @@
|
||||
x(BCH_ERR_invalid_sb, invalid_sb_crypt) \
|
||||
x(BCH_ERR_invalid_sb, invalid_sb_clean) \
|
||||
x(BCH_ERR_invalid_sb, invalid_sb_quota) \
|
||||
x(BCH_ERR_invalid_sb, invalid_sb_errors) \
|
||||
x(BCH_ERR_invalid_sb, invalid_sb_opt_compression) \
|
||||
x(BCH_ERR_invalid, invalid_bkey) \
|
||||
x(BCH_ERR_operation_blocked, nocow_lock_blocked) \
|
||||
|
@ -56,8 +56,9 @@ void bch2_io_error_work(struct work_struct *work)
|
||||
up_write(&c->state_lock);
|
||||
}
|
||||
|
||||
void bch2_io_error(struct bch_dev *ca)
|
||||
void bch2_io_error(struct bch_dev *ca, enum bch_member_error_type type)
|
||||
{
|
||||
atomic64_inc(&ca->errors[type]);
|
||||
//queue_work(system_long_wq, &ca->io_error_work);
|
||||
}
|
||||
|
||||
@ -116,31 +117,34 @@ static struct fsck_err_state *fsck_err_get(struct bch_fs *c, const char *fmt)
|
||||
if (test_bit(BCH_FS_FSCK_DONE, &c->flags))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(s, &c->fsck_errors, list)
|
||||
list_for_each_entry(s, &c->fsck_error_msgs, list)
|
||||
if (s->fmt == fmt) {
|
||||
/*
|
||||
* move it to the head of the list: repeated fsck errors
|
||||
* are common
|
||||
*/
|
||||
list_move(&s->list, &c->fsck_errors);
|
||||
list_move(&s->list, &c->fsck_error_msgs);
|
||||
return s;
|
||||
}
|
||||
|
||||
s = kzalloc(sizeof(*s), GFP_NOFS);
|
||||
if (!s) {
|
||||
if (!c->fsck_alloc_err)
|
||||
if (!c->fsck_alloc_msgs_err)
|
||||
bch_err(c, "kmalloc err, cannot ratelimit fsck errs");
|
||||
c->fsck_alloc_err = true;
|
||||
c->fsck_alloc_msgs_err = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&s->list);
|
||||
s->fmt = fmt;
|
||||
list_add(&s->list, &c->fsck_errors);
|
||||
list_add(&s->list, &c->fsck_error_msgs);
|
||||
return s;
|
||||
}
|
||||
|
||||
int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...)
|
||||
int bch2_fsck_err(struct bch_fs *c,
|
||||
enum bch_fsck_flags flags,
|
||||
enum bch_sb_error_id err,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct fsck_err_state *s = NULL;
|
||||
va_list args;
|
||||
@ -148,11 +152,13 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...)
|
||||
struct printbuf buf = PRINTBUF, *out = &buf;
|
||||
int ret = -BCH_ERR_fsck_ignore;
|
||||
|
||||
bch2_sb_error_count(c, err);
|
||||
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(out, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
mutex_lock(&c->fsck_error_lock);
|
||||
mutex_lock(&c->fsck_error_msgs_lock);
|
||||
s = fsck_err_get(c, fmt);
|
||||
if (s) {
|
||||
/*
|
||||
@ -162,7 +168,7 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...)
|
||||
*/
|
||||
if (s->last_msg && !strcmp(buf.buf, s->last_msg)) {
|
||||
ret = s->ret;
|
||||
mutex_unlock(&c->fsck_error_lock);
|
||||
mutex_unlock(&c->fsck_error_msgs_lock);
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
}
|
||||
@ -257,7 +263,7 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...)
|
||||
if (s)
|
||||
s->ret = ret;
|
||||
|
||||
mutex_unlock(&c->fsck_error_lock);
|
||||
mutex_unlock(&c->fsck_error_msgs_lock);
|
||||
|
||||
printbuf_exit(&buf);
|
||||
|
||||
@ -278,9 +284,9 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
|
||||
{
|
||||
struct fsck_err_state *s, *n;
|
||||
|
||||
mutex_lock(&c->fsck_error_lock);
|
||||
mutex_lock(&c->fsck_error_msgs_lock);
|
||||
|
||||
list_for_each_entry_safe(s, n, &c->fsck_errors, list) {
|
||||
list_for_each_entry_safe(s, n, &c->fsck_error_msgs, list) {
|
||||
if (s->ratelimited && s->last_msg)
|
||||
bch_err(c, "Saw %llu errors like:\n %s", s->nr, s->last_msg);
|
||||
|
||||
@ -289,5 +295,5 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
|
||||
kfree(s);
|
||||
}
|
||||
|
||||
mutex_unlock(&c->fsck_error_lock);
|
||||
mutex_unlock(&c->fsck_error_msgs_lock);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/printk.h>
|
||||
#include "sb-errors.h"
|
||||
|
||||
struct bch_dev;
|
||||
struct bch_fs;
|
||||
@ -101,18 +102,26 @@ struct fsck_err_state {
|
||||
char *last_msg;
|
||||
};
|
||||
|
||||
#define FSCK_CAN_FIX (1 << 0)
|
||||
#define FSCK_CAN_IGNORE (1 << 1)
|
||||
#define FSCK_NEED_FSCK (1 << 2)
|
||||
#define FSCK_NO_RATELIMIT (1 << 3)
|
||||
enum bch_fsck_flags {
|
||||
FSCK_CAN_FIX = 1 << 0,
|
||||
FSCK_CAN_IGNORE = 1 << 1,
|
||||
FSCK_NEED_FSCK = 1 << 2,
|
||||
FSCK_NO_RATELIMIT = 1 << 3,
|
||||
};
|
||||
|
||||
__printf(3, 4) __cold
|
||||
int bch2_fsck_err(struct bch_fs *, unsigned, const char *, ...);
|
||||
#define fsck_err_count(_c, _err) bch2_sb_err_count(_c, BCH_FSCK_ERR_##_err)
|
||||
|
||||
__printf(4, 5) __cold
|
||||
int bch2_fsck_err(struct bch_fs *,
|
||||
enum bch_fsck_flags,
|
||||
enum bch_sb_error_id,
|
||||
const char *, ...);
|
||||
void bch2_flush_fsck_errs(struct bch_fs *);
|
||||
|
||||
#define __fsck_err(c, _flags, msg, ...) \
|
||||
#define __fsck_err(c, _flags, _err_type, ...) \
|
||||
({ \
|
||||
int _ret = bch2_fsck_err(c, _flags, msg, ##__VA_ARGS__); \
|
||||
int _ret = bch2_fsck_err(c, _flags, BCH_FSCK_ERR_##_err_type, \
|
||||
__VA_ARGS__); \
|
||||
\
|
||||
if (_ret != -BCH_ERR_fsck_fix && \
|
||||
_ret != -BCH_ERR_fsck_ignore) { \
|
||||
@ -127,26 +136,53 @@ void bch2_flush_fsck_errs(struct bch_fs *);
|
||||
|
||||
/* XXX: mark in superblock that filesystem contains errors, if we ignore: */
|
||||
|
||||
#define __fsck_err_on(cond, c, _flags, ...) \
|
||||
(unlikely(cond) ? __fsck_err(c, _flags, ##__VA_ARGS__) : false)
|
||||
#define __fsck_err_on(cond, c, _flags, _err_type, ...) \
|
||||
(unlikely(cond) ? __fsck_err(c, _flags, _err_type, __VA_ARGS__) : false)
|
||||
|
||||
#define need_fsck_err_on(cond, c, ...) \
|
||||
__fsck_err_on(cond, c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ##__VA_ARGS__)
|
||||
#define need_fsck_err_on(cond, c, _err_type, ...) \
|
||||
__fsck_err_on(cond, c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, _err_type, __VA_ARGS__)
|
||||
|
||||
#define need_fsck_err(c, ...) \
|
||||
__fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ##__VA_ARGS__)
|
||||
#define need_fsck_err(c, _err_type, ...) \
|
||||
__fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, _err_type, __VA_ARGS__)
|
||||
|
||||
#define mustfix_fsck_err(c, ...) \
|
||||
__fsck_err(c, FSCK_CAN_FIX, ##__VA_ARGS__)
|
||||
#define mustfix_fsck_err(c, _err_type, ...) \
|
||||
__fsck_err(c, FSCK_CAN_FIX, _err_type, __VA_ARGS__)
|
||||
|
||||
#define mustfix_fsck_err_on(cond, c, ...) \
|
||||
__fsck_err_on(cond, c, FSCK_CAN_FIX, ##__VA_ARGS__)
|
||||
#define mustfix_fsck_err_on(cond, c, _err_type, ...) \
|
||||
__fsck_err_on(cond, c, FSCK_CAN_FIX, _err_type, __VA_ARGS__)
|
||||
|
||||
#define fsck_err(c, ...) \
|
||||
__fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, ##__VA_ARGS__)
|
||||
#define fsck_err(c, _err_type, ...) \
|
||||
__fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, _err_type, __VA_ARGS__)
|
||||
|
||||
#define fsck_err_on(cond, c, ...) \
|
||||
__fsck_err_on(cond, c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, ##__VA_ARGS__)
|
||||
#define fsck_err_on(cond, c, _err_type, ...) \
|
||||
__fsck_err_on(cond, c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, _err_type, __VA_ARGS__)
|
||||
|
||||
static inline void bch2_bkey_fsck_err(struct bch_fs *c,
|
||||
struct printbuf *err_msg,
|
||||
enum bch_sb_error_id err_type,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(err_msg, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
}
|
||||
|
||||
#define bkey_fsck_err(c, _err_msg, _err_type, ...) \
|
||||
do { \
|
||||
prt_printf(_err_msg, __VA_ARGS__); \
|
||||
bch2_sb_error_count(c, BCH_FSCK_ERR_##_err_type); \
|
||||
ret = -BCH_ERR_invalid_bkey; \
|
||||
goto fsck_err; \
|
||||
} while (0)
|
||||
|
||||
#define bkey_fsck_err_on(cond, ...) \
|
||||
do { \
|
||||
if (unlikely(cond)) \
|
||||
bkey_fsck_err(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Fatal errors: these don't indicate a bug, but we can't continue running in RW
|
||||
@ -179,26 +215,26 @@ do { \
|
||||
void bch2_io_error_work(struct work_struct *);
|
||||
|
||||
/* Does the error handling without logging a message */
|
||||
void bch2_io_error(struct bch_dev *);
|
||||
void bch2_io_error(struct bch_dev *, enum bch_member_error_type);
|
||||
|
||||
#define bch2_dev_io_err_on(cond, ca, ...) \
|
||||
#define bch2_dev_io_err_on(cond, ca, _type, ...) \
|
||||
({ \
|
||||
bool _ret = (cond); \
|
||||
\
|
||||
if (_ret) { \
|
||||
bch_err_dev_ratelimited(ca, __VA_ARGS__); \
|
||||
bch2_io_error(ca); \
|
||||
bch2_io_error(ca, _type); \
|
||||
} \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
#define bch2_dev_inum_io_err_on(cond, ca, ...) \
|
||||
#define bch2_dev_inum_io_err_on(cond, ca, _type, ...) \
|
||||
({ \
|
||||
bool _ret = (cond); \
|
||||
\
|
||||
if (_ret) { \
|
||||
bch_err_inum_offset_ratelimited(ca, __VA_ARGS__); \
|
||||
bch2_io_error(ca); \
|
||||
bch2_io_error(ca, _type); \
|
||||
} \
|
||||
_ret; \
|
||||
})
|
||||
|
@ -163,17 +163,19 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
|
||||
|
||||
/* KEY_TYPE_btree_ptr: */
|
||||
|
||||
int bch2_btree_ptr_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_btree_ptr_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
if (bkey_val_u64s(k.k) > BCH_REPLICAS_MAX) {
|
||||
prt_printf(err, "value too big (%zu > %u)",
|
||||
bkey_val_u64s(k.k), BCH_REPLICAS_MAX);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
return bch2_bkey_ptrs_invalid(c, k, flags, err);
|
||||
bkey_fsck_err_on(bkey_val_u64s(k.k) > BCH_REPLICAS_MAX, c, err,
|
||||
btree_ptr_val_too_big,
|
||||
"value too big (%zu > %u)", bkey_val_u64s(k.k), BCH_REPLICAS_MAX);
|
||||
|
||||
ret = bch2_bkey_ptrs_invalid(c, k, flags, err);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
@ -182,17 +184,20 @@ void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
bch2_bkey_ptrs_to_text(out, c, k);
|
||||
}
|
||||
|
||||
int bch2_btree_ptr_v2_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_btree_ptr_v2_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
if (bkey_val_u64s(k.k) > BKEY_BTREE_PTR_VAL_U64s_MAX) {
|
||||
prt_printf(err, "value too big (%zu > %zu)",
|
||||
bkey_val_u64s(k.k), BKEY_BTREE_PTR_VAL_U64s_MAX);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
return bch2_bkey_ptrs_invalid(c, k, flags, err);
|
||||
bkey_fsck_err_on(bkey_val_u64s(k.k) > BKEY_BTREE_PTR_VAL_U64s_MAX, c, err,
|
||||
btree_ptr_v2_val_too_big,
|
||||
"value too big (%zu > %zu)",
|
||||
bkey_val_u64s(k.k), BKEY_BTREE_PTR_VAL_U64s_MAX);
|
||||
|
||||
ret = bch2_bkey_ptrs_invalid(c, k, flags, err);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_btree_ptr_v2_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
@ -373,19 +378,18 @@ bool bch2_extent_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r)
|
||||
|
||||
/* KEY_TYPE_reservation: */
|
||||
|
||||
int bch2_reservation_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_reservation_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
struct bkey_s_c_reservation r = bkey_s_c_to_reservation(k);
|
||||
int ret = 0;
|
||||
|
||||
if (!r.v->nr_replicas || r.v->nr_replicas > BCH_REPLICAS_MAX) {
|
||||
prt_printf(err, "invalid nr_replicas (%u)",
|
||||
r.v->nr_replicas);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(!r.v->nr_replicas || r.v->nr_replicas > BCH_REPLICAS_MAX, c, err,
|
||||
reservation_key_nr_replicas_invalid,
|
||||
"invalid nr_replicas (%u)", r.v->nr_replicas);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_reservation_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
@ -1058,7 +1062,7 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
}
|
||||
}
|
||||
|
||||
static int extent_ptr_invalid(const struct bch_fs *c,
|
||||
static int extent_ptr_invalid(struct bch_fs *c,
|
||||
struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
const struct bch_extent_ptr *ptr,
|
||||
@ -1071,6 +1075,7 @@ static int extent_ptr_invalid(const struct bch_fs *c,
|
||||
u64 bucket;
|
||||
u32 bucket_offset;
|
||||
struct bch_dev *ca;
|
||||
int ret = 0;
|
||||
|
||||
if (!bch2_dev_exists2(c, ptr->dev)) {
|
||||
/*
|
||||
@ -1081,41 +1086,33 @@ static int extent_ptr_invalid(const struct bch_fs *c,
|
||||
if (flags & BKEY_INVALID_WRITE)
|
||||
return 0;
|
||||
|
||||
prt_printf(err, "pointer to invalid device (%u)", ptr->dev);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
bkey_fsck_err(c, err, ptr_to_invalid_device,
|
||||
"pointer to invalid device (%u)", ptr->dev);
|
||||
}
|
||||
|
||||
ca = bch_dev_bkey_exists(c, ptr->dev);
|
||||
bkey_for_each_ptr(ptrs, ptr2)
|
||||
if (ptr != ptr2 && ptr->dev == ptr2->dev) {
|
||||
prt_printf(err, "multiple pointers to same device (%u)", ptr->dev);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(ptr != ptr2 && ptr->dev == ptr2->dev, c, err,
|
||||
ptr_to_duplicate_device,
|
||||
"multiple pointers to same device (%u)", ptr->dev);
|
||||
|
||||
bucket = sector_to_bucket_and_offset(ca, ptr->offset, &bucket_offset);
|
||||
|
||||
if (bucket >= ca->mi.nbuckets) {
|
||||
prt_printf(err, "pointer past last bucket (%llu > %llu)",
|
||||
bucket, ca->mi.nbuckets);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
if (ptr->offset < bucket_to_sector(ca, ca->mi.first_bucket)) {
|
||||
prt_printf(err, "pointer before first bucket (%llu < %u)",
|
||||
bucket, ca->mi.first_bucket);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
if (bucket_offset + size_ondisk > ca->mi.bucket_size) {
|
||||
prt_printf(err, "pointer spans multiple buckets (%u + %u > %u)",
|
||||
bkey_fsck_err_on(bucket >= ca->mi.nbuckets, c, err,
|
||||
ptr_after_last_bucket,
|
||||
"pointer past last bucket (%llu > %llu)", bucket, ca->mi.nbuckets);
|
||||
bkey_fsck_err_on(ptr->offset < bucket_to_sector(ca, ca->mi.first_bucket), c, err,
|
||||
ptr_before_first_bucket,
|
||||
"pointer before first bucket (%llu < %u)", bucket, ca->mi.first_bucket);
|
||||
bkey_fsck_err_on(bucket_offset + size_ondisk > ca->mi.bucket_size, c, err,
|
||||
ptr_spans_multiple_buckets,
|
||||
"pointer spans multiple buckets (%u + %u > %u)",
|
||||
bucket_offset, size_ondisk, ca->mi.bucket_size);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_bkey_ptrs_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
@ -1125,24 +1122,22 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
unsigned size_ondisk = k.k->size;
|
||||
unsigned nonce = UINT_MAX;
|
||||
unsigned nr_ptrs = 0;
|
||||
bool unwritten = false, have_ec = false, crc_since_last_ptr = false;
|
||||
int ret;
|
||||
bool have_written = false, have_unwritten = false, have_ec = false, crc_since_last_ptr = false;
|
||||
int ret = 0;
|
||||
|
||||
if (bkey_is_btree_ptr(k.k))
|
||||
size_ondisk = btree_sectors(c);
|
||||
|
||||
bkey_extent_entry_for_each(ptrs, entry) {
|
||||
if (__extent_entry_type(entry) >= BCH_EXTENT_ENTRY_MAX) {
|
||||
prt_printf(err, "invalid extent entry type (got %u, max %u)",
|
||||
__extent_entry_type(entry), BCH_EXTENT_ENTRY_MAX);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(__extent_entry_type(entry) >= BCH_EXTENT_ENTRY_MAX, c, err,
|
||||
extent_ptrs_invalid_entry,
|
||||
"invalid extent entry type (got %u, max %u)",
|
||||
__extent_entry_type(entry), BCH_EXTENT_ENTRY_MAX);
|
||||
|
||||
if (bkey_is_btree_ptr(k.k) &&
|
||||
!extent_entry_is_ptr(entry)) {
|
||||
prt_printf(err, "has non ptr field");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bkey_is_btree_ptr(k.k) &&
|
||||
!extent_entry_is_ptr(entry), c, err,
|
||||
btree_ptr_has_non_ptr,
|
||||
"has non ptr field");
|
||||
|
||||
switch (extent_entry_type(entry)) {
|
||||
case BCH_EXTENT_ENTRY_ptr:
|
||||
@ -1151,22 +1146,15 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nr_ptrs && unwritten != entry->ptr.unwritten) {
|
||||
prt_printf(err, "extent with unwritten and written ptrs");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(entry->ptr.cached && have_ec, c, err,
|
||||
ptr_cached_and_erasure_coded,
|
||||
"cached, erasure coded ptr");
|
||||
|
||||
if (k.k->type != KEY_TYPE_extent && entry->ptr.unwritten) {
|
||||
prt_printf(err, "has unwritten ptrs");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
if (!entry->ptr.unwritten)
|
||||
have_written = true;
|
||||
else
|
||||
have_unwritten = true;
|
||||
|
||||
if (entry->ptr.cached && have_ec) {
|
||||
prt_printf(err, "cached, erasure coded ptr");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
unwritten = entry->ptr.unwritten;
|
||||
have_ec = false;
|
||||
crc_since_last_ptr = false;
|
||||
nr_ptrs++;
|
||||
@ -1176,52 +1164,41 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
case BCH_EXTENT_ENTRY_crc128:
|
||||
crc = bch2_extent_crc_unpack(k.k, entry_to_crc(entry));
|
||||
|
||||
if (crc.offset + crc.live_size >
|
||||
crc.uncompressed_size) {
|
||||
prt_printf(err, "checksum offset + key size > uncompressed size");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
size_ondisk = crc.compressed_size;
|
||||
|
||||
if (!bch2_checksum_type_valid(c, crc.csum_type)) {
|
||||
prt_printf(err, "invalid checksum type");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
if (crc.compression_type >= BCH_COMPRESSION_TYPE_NR) {
|
||||
prt_printf(err, "invalid compression type");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(crc.offset + crc.live_size > crc.uncompressed_size, c, err,
|
||||
ptr_crc_uncompressed_size_too_small,
|
||||
"checksum offset + key size > uncompressed size");
|
||||
bkey_fsck_err_on(!bch2_checksum_type_valid(c, crc.csum_type), c, err,
|
||||
ptr_crc_csum_type_unknown,
|
||||
"invalid checksum type");
|
||||
bkey_fsck_err_on(crc.compression_type >= BCH_COMPRESSION_TYPE_NR, c, err,
|
||||
ptr_crc_compression_type_unknown,
|
||||
"invalid compression type");
|
||||
|
||||
if (bch2_csum_type_is_encryption(crc.csum_type)) {
|
||||
if (nonce == UINT_MAX)
|
||||
nonce = crc.offset + crc.nonce;
|
||||
else if (nonce != crc.offset + crc.nonce) {
|
||||
prt_printf(err, "incorrect nonce");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
else if (nonce != crc.offset + crc.nonce)
|
||||
bkey_fsck_err(c, err, ptr_crc_nonce_mismatch,
|
||||
"incorrect nonce");
|
||||
}
|
||||
|
||||
if (crc_since_last_ptr) {
|
||||
prt_printf(err, "redundant crc entry");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(crc_since_last_ptr, c, err,
|
||||
ptr_crc_redundant,
|
||||
"redundant crc entry");
|
||||
crc_since_last_ptr = true;
|
||||
|
||||
if (crc_is_encoded(crc) &&
|
||||
(crc.uncompressed_size > c->opts.encoded_extent_max >> 9) &&
|
||||
(flags & (BKEY_INVALID_WRITE|BKEY_INVALID_COMMIT))) {
|
||||
prt_printf(err, "too large encoded extent");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(crc_is_encoded(crc) &&
|
||||
(crc.uncompressed_size > c->opts.encoded_extent_max >> 9) &&
|
||||
(flags & (BKEY_INVALID_WRITE|BKEY_INVALID_COMMIT)), c, err,
|
||||
ptr_crc_uncompressed_size_too_big,
|
||||
"too large encoded extent");
|
||||
|
||||
size_ondisk = crc.compressed_size;
|
||||
break;
|
||||
case BCH_EXTENT_ENTRY_stripe_ptr:
|
||||
if (have_ec) {
|
||||
prt_printf(err, "redundant stripe entry");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(have_ec, c, err,
|
||||
ptr_stripe_redundant,
|
||||
"redundant stripe entry");
|
||||
have_ec = true;
|
||||
break;
|
||||
case BCH_EXTENT_ENTRY_rebalance: {
|
||||
@ -1238,27 +1215,26 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
}
|
||||
}
|
||||
|
||||
if (!nr_ptrs) {
|
||||
prt_str(err, "no ptrs");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
if (nr_ptrs >= BCH_BKEY_PTRS_MAX) {
|
||||
prt_str(err, "too many ptrs");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
if (crc_since_last_ptr) {
|
||||
prt_printf(err, "redundant crc entry");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
if (have_ec) {
|
||||
prt_printf(err, "redundant stripe entry");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(!nr_ptrs, c, err,
|
||||
extent_ptrs_no_ptrs,
|
||||
"no ptrs");
|
||||
bkey_fsck_err_on(nr_ptrs > BCH_BKEY_PTRS_MAX, c, err,
|
||||
extent_ptrs_too_many_ptrs,
|
||||
"too many ptrs: %u > %u", nr_ptrs, BCH_BKEY_PTRS_MAX);
|
||||
bkey_fsck_err_on(have_written && have_unwritten, c, err,
|
||||
extent_ptrs_written_and_unwritten,
|
||||
"extent with unwritten and written ptrs");
|
||||
bkey_fsck_err_on(k.k->type != KEY_TYPE_extent && have_unwritten, c, err,
|
||||
extent_ptrs_unwritten,
|
||||
"has unwritten ptrs");
|
||||
bkey_fsck_err_on(crc_since_last_ptr, c, err,
|
||||
extent_ptrs_redundant_crc,
|
||||
"redundant crc entry");
|
||||
bkey_fsck_err_on(have_ec, c, err,
|
||||
extent_ptrs_redundant_stripe,
|
||||
"redundant stripe entry");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_ptr_swab(struct bkey_s k)
|
||||
|
@ -400,12 +400,12 @@ int bch2_bkey_pick_read_device(struct bch_fs *, struct bkey_s_c,
|
||||
|
||||
/* KEY_TYPE_btree_ptr: */
|
||||
|
||||
int bch2_btree_ptr_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_btree_ptr_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_btree_ptr_to_text(struct printbuf *, struct bch_fs *,
|
||||
struct bkey_s_c);
|
||||
|
||||
int bch2_btree_ptr_v2_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_btree_ptr_v2_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_btree_ptr_v2_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned,
|
||||
@ -445,7 +445,7 @@ bool bch2_extent_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
|
||||
|
||||
/* KEY_TYPE_reservation: */
|
||||
|
||||
int bch2_reservation_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_reservation_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_reservation_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
bool bch2_reservation_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
|
||||
@ -705,7 +705,7 @@ void bch2_extent_ptr_set_cached(struct bkey_s, struct bch_extent_ptr *);
|
||||
bool bch2_extent_normalize(struct bch_fs *, struct bkey_s);
|
||||
void bch2_bkey_ptrs_to_text(struct printbuf *, struct bch_fs *,
|
||||
struct bkey_s_c);
|
||||
int bch2_bkey_ptrs_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_bkey_ptrs_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
|
||||
void bch2_ptr_swab(struct bkey_s);
|
||||
|
@ -721,8 +721,9 @@ static int check_key_has_snapshot(struct btree_trans *trans,
|
||||
int ret = 0;
|
||||
|
||||
if (mustfix_fsck_err_on(!bch2_snapshot_equiv(c, k.k->p.snapshot), c,
|
||||
"key in missing snapshot: %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
bkey_in_missing_snapshot,
|
||||
"key in missing snapshot: %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
ret = bch2_btree_delete_at(trans, iter,
|
||||
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?: 1;
|
||||
fsck_err:
|
||||
@ -791,6 +792,7 @@ static int hash_check_key(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(k.k->type == desc.key_type &&
|
||||
!desc.cmp_bkey(k, hash_k), c,
|
||||
hash_table_key_duplicate,
|
||||
"duplicate hash table keys:\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, hash_k),
|
||||
@ -809,7 +811,8 @@ out:
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
bad_hash:
|
||||
if (fsck_err(c, "hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s",
|
||||
if (fsck_err(c, hash_table_key_wrong_offset,
|
||||
"hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s",
|
||||
bch2_btree_id_str(desc.btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
|
||||
@ -860,7 +863,8 @@ static int check_inode(struct btree_trans *trans,
|
||||
*prev = u;
|
||||
|
||||
if (fsck_err_on(prev->bi_hash_seed != u.bi_hash_seed ||
|
||||
inode_d_type(prev) != inode_d_type(&u), c,
|
||||
inode_d_type(prev) != inode_d_type(&u),
|
||||
c, inode_snapshot_mismatch,
|
||||
"inodes in different snapshots don't match")) {
|
||||
bch_err(c, "repair not implemented yet");
|
||||
return -EINVAL;
|
||||
@ -888,7 +892,8 @@ static int check_inode(struct btree_trans *trans,
|
||||
|
||||
if (u.bi_flags & BCH_INODE_UNLINKED &&
|
||||
(!c->sb.clean ||
|
||||
fsck_err(c, "filesystem marked clean, but inode %llu unlinked",
|
||||
fsck_err(c, inode_unlinked_but_clean,
|
||||
"filesystem marked clean, but inode %llu unlinked",
|
||||
u.bi_inum))) {
|
||||
bch2_trans_unlock(trans);
|
||||
bch2_fs_lazy_rw(c);
|
||||
@ -900,7 +905,8 @@ static int check_inode(struct btree_trans *trans,
|
||||
|
||||
if (u.bi_flags & BCH_INODE_I_SIZE_DIRTY &&
|
||||
(!c->sb.clean ||
|
||||
fsck_err(c, "filesystem marked clean, but inode %llu has i_size dirty",
|
||||
fsck_err(c, inode_i_size_dirty_but_clean,
|
||||
"filesystem marked clean, but inode %llu has i_size dirty",
|
||||
u.bi_inum))) {
|
||||
bch_verbose(c, "truncating inode %llu", u.bi_inum);
|
||||
|
||||
@ -932,7 +938,8 @@ static int check_inode(struct btree_trans *trans,
|
||||
|
||||
if (u.bi_flags & BCH_INODE_I_SECTORS_DIRTY &&
|
||||
(!c->sb.clean ||
|
||||
fsck_err(c, "filesystem marked clean, but inode %llu has i_sectors dirty",
|
||||
fsck_err(c, inode_i_sectors_dirty_but_clean,
|
||||
"filesystem marked clean, but inode %llu has i_sectors dirty",
|
||||
u.bi_inum))) {
|
||||
s64 sectors;
|
||||
|
||||
@ -1058,10 +1065,11 @@ static int check_i_sectors(struct btree_trans *trans, struct inode_walker *w)
|
||||
return -BCH_ERR_internal_fsck_err;
|
||||
}
|
||||
|
||||
if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_I_SECTORS_DIRTY), c,
|
||||
"inode %llu:%u has incorrect i_sectors: got %llu, should be %llu",
|
||||
w->last_pos.inode, i->snapshot,
|
||||
i->inode.bi_sectors, i->count)) {
|
||||
if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_I_SECTORS_DIRTY),
|
||||
c, inode_i_sectors_wrong,
|
||||
"inode %llu:%u has incorrect i_sectors: got %llu, should be %llu",
|
||||
w->last_pos.inode, i->snapshot,
|
||||
i->inode.bi_sectors, i->count)) {
|
||||
i->inode.bi_sectors = i->count;
|
||||
ret = fsck_write_inode(trans, &i->inode, i->snapshot);
|
||||
if (ret)
|
||||
@ -1202,7 +1210,8 @@ static int overlapping_extents_found(struct btree_trans *trans,
|
||||
prt_printf(&buf, "\n overwriting %s extent",
|
||||
pos1.snapshot >= pos2.p.snapshot ? "first" : "second");
|
||||
|
||||
if (fsck_err(c, "overlapping extents%s", buf.buf)) {
|
||||
if (fsck_err(c, extent_overlapping,
|
||||
"overlapping extents%s", buf.buf)) {
|
||||
struct btree_iter *old_iter = &iter1;
|
||||
struct disk_reservation res = { 0 };
|
||||
|
||||
@ -1357,7 +1366,7 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
goto err;
|
||||
|
||||
if (k.k->type != KEY_TYPE_whiteout) {
|
||||
if (fsck_err_on(!i, c,
|
||||
if (fsck_err_on(!i, c, extent_in_missing_inode,
|
||||
"extent in missing inode:\n %s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
@ -1365,7 +1374,8 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
|
||||
if (fsck_err_on(i &&
|
||||
!S_ISREG(i->inode.bi_mode) &&
|
||||
!S_ISLNK(i->inode.bi_mode), c,
|
||||
!S_ISLNK(i->inode.bi_mode),
|
||||
c, extent_in_non_reg_inode,
|
||||
"extent in non regular inode mode %o:\n %s",
|
||||
i->inode.bi_mode,
|
||||
(printbuf_reset(&buf),
|
||||
@ -1397,7 +1407,8 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (k.k->type != KEY_TYPE_whiteout) {
|
||||
if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_I_SIZE_DIRTY) &&
|
||||
k.k->p.offset > round_up(i->inode.bi_size, block_bytes(c)) >> 9 &&
|
||||
!bkey_extent_is_reservation(k), c,
|
||||
!bkey_extent_is_reservation(k),
|
||||
c, extent_past_end_of_inode,
|
||||
"extent type past end of inode %llu:%u, i_size %llu\n %s",
|
||||
i->inode.bi_inum, i->snapshot, i->inode.bi_size,
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
@ -1519,7 +1530,8 @@ static int check_subdir_count(struct btree_trans *trans, struct inode_walker *w)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fsck_err_on(i->inode.bi_nlink != i->count, c,
|
||||
if (fsck_err_on(i->inode.bi_nlink != i->count,
|
||||
c, inode_dir_wrong_nlink,
|
||||
"directory %llu:%u with wrong i_nlink: got %u, should be %llu",
|
||||
w->last_pos.inode, i->snapshot, i->inode.bi_nlink, i->count)) {
|
||||
i->inode.bi_nlink = i->count;
|
||||
@ -1563,16 +1575,16 @@ static int check_dirent_target(struct btree_trans *trans,
|
||||
backpointer_exists = ret;
|
||||
ret = 0;
|
||||
|
||||
if (fsck_err_on(S_ISDIR(target->bi_mode) &&
|
||||
backpointer_exists, c,
|
||||
if (fsck_err_on(S_ISDIR(target->bi_mode) && backpointer_exists,
|
||||
c, inode_dir_multiple_links,
|
||||
"directory %llu with multiple links",
|
||||
target->bi_inum)) {
|
||||
ret = __remove_dirent(trans, d.k->p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fsck_err_on(backpointer_exists &&
|
||||
!target->bi_nlink, c,
|
||||
if (fsck_err_on(backpointer_exists && !target->bi_nlink,
|
||||
c, inode_multiple_links_but_nlink_0,
|
||||
"inode %llu type %s has multiple links but i_nlink 0",
|
||||
target->bi_inum, bch2_d_types[d.v->d_type])) {
|
||||
target->bi_nlink++;
|
||||
@ -1583,7 +1595,8 @@ static int check_dirent_target(struct btree_trans *trans,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (fsck_err_on(!backpointer_exists, c,
|
||||
if (fsck_err_on(!backpointer_exists,
|
||||
c, inode_wrong_backpointer,
|
||||
"inode %llu:%u has wrong backpointer:\n"
|
||||
"got %llu:%llu\n"
|
||||
"should be %llu:%llu",
|
||||
@ -1601,7 +1614,8 @@ static int check_dirent_target(struct btree_trans *trans,
|
||||
}
|
||||
}
|
||||
|
||||
if (fsck_err_on(d.v->d_type != inode_d_type(target), c,
|
||||
if (fsck_err_on(d.v->d_type != inode_d_type(target),
|
||||
c, dirent_d_type_wrong,
|
||||
"incorrect d_type: got %s, should be %s:\n%s",
|
||||
bch2_d_type_str(d.v->d_type),
|
||||
bch2_d_type_str(inode_d_type(target)),
|
||||
@ -1625,7 +1639,8 @@ static int check_dirent_target(struct btree_trans *trans,
|
||||
if (d.v->d_type == DT_SUBVOL &&
|
||||
target->bi_parent_subvol != le32_to_cpu(d.v->d_parent_subvol) &&
|
||||
(c->sb.version < bcachefs_metadata_version_subvol_dirent ||
|
||||
fsck_err(c, "dirent has wrong d_parent_subvol field: got %u, should be %u",
|
||||
fsck_err(c, dirent_d_parent_subvol_wrong,
|
||||
"dirent has wrong d_parent_subvol field: got %u, should be %u",
|
||||
le32_to_cpu(d.v->d_parent_subvol),
|
||||
target->bi_parent_subvol))) {
|
||||
n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
|
||||
@ -1697,7 +1712,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
*hash_info = bch2_hash_info_init(c, &dir->inodes.data[0].inode);
|
||||
dir->first_this_inode = false;
|
||||
|
||||
if (fsck_err_on(!i, c,
|
||||
if (fsck_err_on(!i, c, dirent_in_missing_dir_inode,
|
||||
"dirent in nonexisting directory:\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
@ -1709,7 +1724,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (!i)
|
||||
goto out;
|
||||
|
||||
if (fsck_err_on(!S_ISDIR(i->inode.bi_mode), c,
|
||||
if (fsck_err_on(!S_ISDIR(i->inode.bi_mode),
|
||||
c, dirent_in_non_dir_inode,
|
||||
"dirent in non directory inode type %s:\n%s",
|
||||
bch2_d_type_str(inode_d_type(&i->inode)),
|
||||
(printbuf_reset(&buf),
|
||||
@ -1743,7 +1759,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(ret, c,
|
||||
if (fsck_err_on(ret, c, dirent_to_missing_subvol,
|
||||
"dirent points to missing subvolume %u",
|
||||
le32_to_cpu(d.v->d_child_subvol))) {
|
||||
ret = __remove_dirent(trans, d.k->p);
|
||||
@ -1755,7 +1771,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(ret, c,
|
||||
if (fsck_err_on(ret, c, subvol_to_missing_root,
|
||||
"subvolume %u points to missing subvolume root %llu",
|
||||
target_subvol,
|
||||
target_inum)) {
|
||||
@ -1764,7 +1780,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (fsck_err_on(subvol_root.bi_subvol != target_subvol, c,
|
||||
if (fsck_err_on(subvol_root.bi_subvol != target_subvol,
|
||||
c, subvol_root_wrong_bi_subvol,
|
||||
"subvol root %llu has wrong bi_subvol field: got %u, should be %u",
|
||||
target_inum,
|
||||
subvol_root.bi_subvol, target_subvol)) {
|
||||
@ -1783,7 +1800,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(!target->inodes.nr, c,
|
||||
if (fsck_err_on(!target->inodes.nr,
|
||||
c, dirent_to_missing_inode,
|
||||
"dirent points to missing inode: (equiv %u)\n%s",
|
||||
equiv.snapshot,
|
||||
(printbuf_reset(&buf),
|
||||
@ -1869,7 +1887,7 @@ static int check_xattr(struct btree_trans *trans, struct btree_iter *iter,
|
||||
*hash_info = bch2_hash_info_init(c, &inode->inodes.data[0].inode);
|
||||
inode->first_this_inode = false;
|
||||
|
||||
if (fsck_err_on(!i, c,
|
||||
if (fsck_err_on(!i, c, xattr_in_missing_inode,
|
||||
"xattr for missing inode %llu",
|
||||
k.k->p.inode))
|
||||
return bch2_btree_delete_at(trans, iter, 0);
|
||||
@ -1918,7 +1936,8 @@ static int check_root_trans(struct btree_trans *trans)
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
return ret;
|
||||
|
||||
if (mustfix_fsck_err_on(ret, c, "root subvol missing")) {
|
||||
if (mustfix_fsck_err_on(ret, c, root_subvol_missing,
|
||||
"root subvol missing")) {
|
||||
struct bkey_i_subvolume root_subvol;
|
||||
|
||||
snapshot = U32_MAX;
|
||||
@ -1944,8 +1963,10 @@ static int check_root_trans(struct btree_trans *trans)
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
return ret;
|
||||
|
||||
if (mustfix_fsck_err_on(ret, c, "root directory missing") ||
|
||||
mustfix_fsck_err_on(!S_ISDIR(root_inode.bi_mode), c,
|
||||
if (mustfix_fsck_err_on(ret, c, root_dir_missing,
|
||||
"root directory missing") ||
|
||||
mustfix_fsck_err_on(!S_ISDIR(root_inode.bi_mode),
|
||||
c, root_inode_not_dir,
|
||||
"root inode not a directory")) {
|
||||
bch2_inode_init(c, &root_inode, 0, 0, S_IFDIR|0755,
|
||||
0, NULL);
|
||||
@ -2049,7 +2070,8 @@ static int check_path(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (bch2_err_matches(ret, ENOENT)) {
|
||||
if (fsck_err(c, "unreachable inode %llu:%u, type %s nlink %u backptr %llu:%llu",
|
||||
if (fsck_err(c, inode_unreachable,
|
||||
"unreachable inode %llu:%u, type %s nlink %u backptr %llu:%llu",
|
||||
inode->bi_inum, snapshot,
|
||||
bch2_d_type_str(inode_d_type(inode)),
|
||||
inode->bi_nlink,
|
||||
@ -2089,7 +2111,8 @@ static int check_path(struct btree_trans *trans,
|
||||
pr_err("%llu:%u", i->inum, i->snapshot);
|
||||
pr_err("%llu:%u", inode->bi_inum, snapshot);
|
||||
|
||||
if (!fsck_err(c, "directory structure loop"))
|
||||
if (!fsck_err(c, dir_loop,
|
||||
"directory structure loop"))
|
||||
return 0;
|
||||
|
||||
ret = commit_do(trans, NULL, NULL,
|
||||
@ -2349,7 +2372,8 @@ static int check_nlinks_update_inode(struct btree_trans *trans, struct btree_ite
|
||||
link = &links->d[++*idx];
|
||||
}
|
||||
|
||||
if (fsck_err_on(bch2_inode_nlink_get(&u) != link->count, c,
|
||||
if (fsck_err_on(bch2_inode_nlink_get(&u) != link->count,
|
||||
c, inode_wrong_nlink,
|
||||
"inode %llu type %s has wrong i_nlink (%u, should be %u)",
|
||||
u.bi_inum, bch2_d_types[mode_to_type(u.bi_mode)],
|
||||
bch2_inode_nlink_get(&u), link->count)) {
|
||||
|
@ -398,104 +398,102 @@ struct bkey_i *bch2_inode_to_v3(struct btree_trans *trans, struct bkey_i *k)
|
||||
return &inode_p->inode.k_i;
|
||||
}
|
||||
|
||||
static int __bch2_inode_invalid(struct bkey_s_c k, struct printbuf *err)
|
||||
static int __bch2_inode_invalid(struct bch_fs *c, struct bkey_s_c k, struct printbuf *err)
|
||||
{
|
||||
struct bch_inode_unpacked unpacked;
|
||||
int ret = 0;
|
||||
|
||||
if (k.k->p.inode) {
|
||||
prt_printf(err, "nonzero k.p.inode");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(k.k->p.inode, c, err,
|
||||
inode_pos_inode_nonzero,
|
||||
"nonzero k.p.inode");
|
||||
|
||||
if (k.k->p.offset < BLOCKDEV_INODE_MAX) {
|
||||
prt_printf(err, "fs inode in blockdev range");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(k.k->p.offset < BLOCKDEV_INODE_MAX, c, err,
|
||||
inode_pos_blockdev_range,
|
||||
"fs inode in blockdev range");
|
||||
|
||||
if (bch2_inode_unpack(k, &unpacked)) {
|
||||
prt_printf(err, "invalid variable length fields");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bch2_inode_unpack(k, &unpacked), c, err,
|
||||
inode_unpack_error,
|
||||
"invalid variable length fields");
|
||||
|
||||
if (unpacked.bi_data_checksum >= BCH_CSUM_OPT_NR + 1) {
|
||||
prt_printf(err, "invalid data checksum type (%u >= %u",
|
||||
unpacked.bi_data_checksum, BCH_CSUM_OPT_NR + 1);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(unpacked.bi_data_checksum >= BCH_CSUM_OPT_NR + 1, c, err,
|
||||
inode_checksum_type_invalid,
|
||||
"invalid data checksum type (%u >= %u",
|
||||
unpacked.bi_data_checksum, BCH_CSUM_OPT_NR + 1);
|
||||
|
||||
if (unpacked.bi_compression &&
|
||||
!bch2_compression_opt_valid(unpacked.bi_compression - 1)) {
|
||||
prt_printf(err, "invalid compression opt %u",
|
||||
unpacked.bi_compression - 1);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(unpacked.bi_compression &&
|
||||
!bch2_compression_opt_valid(unpacked.bi_compression - 1), c, err,
|
||||
inode_compression_type_invalid,
|
||||
"invalid compression opt %u", unpacked.bi_compression - 1);
|
||||
|
||||
if ((unpacked.bi_flags & BCH_INODE_UNLINKED) &&
|
||||
unpacked.bi_nlink != 0) {
|
||||
prt_printf(err, "flagged as unlinked but bi_nlink != 0");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on((unpacked.bi_flags & BCH_INODE_UNLINKED) &&
|
||||
unpacked.bi_nlink != 0, c, err,
|
||||
inode_unlinked_but_nlink_nonzero,
|
||||
"flagged as unlinked but bi_nlink != 0");
|
||||
|
||||
if (unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode)) {
|
||||
prt_printf(err, "subvolume root but not a directory");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode), c, err,
|
||||
inode_subvol_root_but_not_dir,
|
||||
"subvolume root but not a directory");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_inode_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
struct bkey_s_c_inode inode = bkey_s_c_to_inode(k);
|
||||
int ret = 0;
|
||||
|
||||
if (INODE_STR_HASH(inode.v) >= BCH_STR_HASH_NR) {
|
||||
prt_printf(err, "invalid str hash type (%llu >= %u)",
|
||||
INODE_STR_HASH(inode.v), BCH_STR_HASH_NR);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(INODE_STR_HASH(inode.v) >= BCH_STR_HASH_NR, c, err,
|
||||
inode_str_hash_invalid,
|
||||
"invalid str hash type (%llu >= %u)",
|
||||
INODE_STR_HASH(inode.v), BCH_STR_HASH_NR);
|
||||
|
||||
return __bch2_inode_invalid(k, err);
|
||||
ret = __bch2_inode_invalid(c, k, err);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_inode_v2_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_inode_v2_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
struct bkey_s_c_inode_v2 inode = bkey_s_c_to_inode_v2(k);
|
||||
int ret = 0;
|
||||
|
||||
if (INODEv2_STR_HASH(inode.v) >= BCH_STR_HASH_NR) {
|
||||
prt_printf(err, "invalid str hash type (%llu >= %u)",
|
||||
INODEv2_STR_HASH(inode.v), BCH_STR_HASH_NR);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(INODEv2_STR_HASH(inode.v) >= BCH_STR_HASH_NR, c, err,
|
||||
inode_str_hash_invalid,
|
||||
"invalid str hash type (%llu >= %u)",
|
||||
INODEv2_STR_HASH(inode.v), BCH_STR_HASH_NR);
|
||||
|
||||
return __bch2_inode_invalid(k, err);
|
||||
ret = __bch2_inode_invalid(c, k, err);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_inode_v3_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_inode_v3_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
struct bkey_s_c_inode_v3 inode = bkey_s_c_to_inode_v3(k);
|
||||
int ret = 0;
|
||||
|
||||
if (INODEv3_FIELDS_START(inode.v) < INODEv3_FIELDS_START_INITIAL ||
|
||||
INODEv3_FIELDS_START(inode.v) > bkey_val_u64s(inode.k)) {
|
||||
prt_printf(err, "invalid fields_start (got %llu, min %u max %zu)",
|
||||
INODEv3_FIELDS_START(inode.v),
|
||||
INODEv3_FIELDS_START_INITIAL,
|
||||
bkey_val_u64s(inode.k));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(INODEv3_FIELDS_START(inode.v) < INODEv3_FIELDS_START_INITIAL ||
|
||||
INODEv3_FIELDS_START(inode.v) > bkey_val_u64s(inode.k), c, err,
|
||||
inode_v3_fields_start_bad,
|
||||
"invalid fields_start (got %llu, min %u max %zu)",
|
||||
INODEv3_FIELDS_START(inode.v),
|
||||
INODEv3_FIELDS_START_INITIAL,
|
||||
bkey_val_u64s(inode.k));
|
||||
|
||||
if (INODEv3_STR_HASH(inode.v) >= BCH_STR_HASH_NR) {
|
||||
prt_printf(err, "invalid str hash type (%llu >= %u)",
|
||||
INODEv3_STR_HASH(inode.v), BCH_STR_HASH_NR);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(INODEv3_STR_HASH(inode.v) >= BCH_STR_HASH_NR, c, err,
|
||||
inode_str_hash_invalid,
|
||||
"invalid str hash type (%llu >= %u)",
|
||||
INODEv3_STR_HASH(inode.v), BCH_STR_HASH_NR);
|
||||
|
||||
return __bch2_inode_invalid(k, err);
|
||||
ret = __bch2_inode_invalid(c, k, err);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __bch2_inode_unpacked_to_text(struct printbuf *out,
|
||||
@ -612,16 +610,17 @@ int bch2_mark_inode(struct btree_trans *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bch2_inode_generation_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_inode_generation_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
if (k.k->p.inode) {
|
||||
prt_printf(err, "nonzero k.p.inode");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(k.k->p.inode, c, err,
|
||||
inode_pos_inode_nonzero,
|
||||
"nonzero k.p.inode");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_inode_generation_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
@ -1068,6 +1067,7 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
|
||||
return 0;
|
||||
|
||||
if (!fsck_err_on(c->sb.clean, c,
|
||||
deleted_inode_but_clean,
|
||||
"filesystem marked as clean but have deleted inode %llu:%u",
|
||||
pos.offset, pos.snapshot))
|
||||
return 0;
|
||||
@ -1079,6 +1079,7 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
|
||||
|
||||
ret = bkey_is_inode(k.k) ? 0 : -BCH_ERR_ENOENT_inode;
|
||||
if (fsck_err_on(!bkey_is_inode(k.k), c,
|
||||
deleted_inode_missing,
|
||||
"nonexistent inode %llu:%u in deleted_inodes btree",
|
||||
pos.offset, pos.snapshot))
|
||||
goto delete;
|
||||
@ -1088,11 +1089,13 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(S_ISDIR(inode.bi_mode), c,
|
||||
deleted_inode_is_dir,
|
||||
"directory %llu:%u in deleted_inodes btree",
|
||||
pos.offset, pos.snapshot))
|
||||
goto delete;
|
||||
|
||||
if (fsck_err_on(!(inode.bi_flags & BCH_INODE_UNLINKED), c,
|
||||
deleted_inode_not_unlinked,
|
||||
"non-deleted inode %llu:%u in deleted_inodes btree",
|
||||
pos.offset, pos.snapshot))
|
||||
goto delete;
|
||||
|
@ -8,11 +8,11 @@
|
||||
enum bkey_invalid_flags;
|
||||
extern const char * const bch2_inode_opts[];
|
||||
|
||||
int bch2_inode_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_inode_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
int bch2_inode_v2_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_inode_v2_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
int bch2_inode_v3_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_inode_v3_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_inode_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
@ -52,7 +52,7 @@ static inline bool bkey_is_inode(const struct bkey *k)
|
||||
k->type == KEY_TYPE_inode_v3;
|
||||
}
|
||||
|
||||
int bch2_inode_generation_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_inode_generation_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_inode_generation_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
|
@ -643,7 +643,7 @@ csum_err:
|
||||
"data checksum error: expected %0llx:%0llx got %0llx:%0llx (type %s)",
|
||||
rbio->pick.crc.csum.hi, rbio->pick.crc.csum.lo,
|
||||
csum.hi, csum.lo, bch2_csum_types[crc.csum_type]);
|
||||
bch2_io_error(ca);
|
||||
bch2_io_error(ca, BCH_MEMBER_ERROR_checksum);
|
||||
bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
|
||||
goto out;
|
||||
decompression_err:
|
||||
@ -677,7 +677,7 @@ static void bch2_read_endio(struct bio *bio)
|
||||
if (!rbio->split)
|
||||
rbio->bio.bi_end_io = rbio->end_io;
|
||||
|
||||
if (bch2_dev_inum_io_err_on(bio->bi_status, ca,
|
||||
if (bch2_dev_inum_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_read,
|
||||
rbio->read_pos.inode,
|
||||
rbio->read_pos.offset,
|
||||
"data read error: %s",
|
||||
|
@ -637,7 +637,7 @@ static void bch2_write_endio(struct bio *bio)
|
||||
struct bch_fs *c = wbio->c;
|
||||
struct bch_dev *ca = bch_dev_bkey_exists(c, wbio->dev);
|
||||
|
||||
if (bch2_dev_inum_io_err_on(bio->bi_status, ca,
|
||||
if (bch2_dev_inum_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write,
|
||||
op->pos.inode,
|
||||
wbio->inode_offset << 9,
|
||||
"data write error: %s",
|
||||
|
@ -140,7 +140,8 @@ static int journal_entry_add(struct bch_fs *c, struct bch_dev *ca,
|
||||
if (!dup->csum_good)
|
||||
goto replace;
|
||||
|
||||
fsck_err(c, "found duplicate but non identical journal entries (seq %llu)",
|
||||
fsck_err(c, journal_entry_replicas_data_mismatch,
|
||||
"found duplicate but non identical journal entries (seq %llu)",
|
||||
le64_to_cpu(j->seq));
|
||||
i = dup;
|
||||
goto found;
|
||||
@ -235,7 +236,7 @@ static void journal_entry_err_msg(struct printbuf *out,
|
||||
prt_str(out, ": ");
|
||||
}
|
||||
|
||||
#define journal_entry_err(c, version, jset, entry, msg, ...) \
|
||||
#define journal_entry_err(c, version, jset, entry, _err, msg, ...) \
|
||||
({ \
|
||||
struct printbuf _buf = PRINTBUF; \
|
||||
\
|
||||
@ -244,9 +245,10 @@ static void journal_entry_err_msg(struct printbuf *out,
|
||||
\
|
||||
switch (flags & BKEY_INVALID_WRITE) { \
|
||||
case READ: \
|
||||
mustfix_fsck_err(c, "%s", _buf.buf); \
|
||||
mustfix_fsck_err(c, _err, "%s", _buf.buf); \
|
||||
break; \
|
||||
case WRITE: \
|
||||
bch2_sb_error_count(c, BCH_FSCK_ERR_##_err); \
|
||||
bch_err(c, "corrupt metadata before write: %s\n", _buf.buf);\
|
||||
if (bch2_fs_inconsistent(c)) { \
|
||||
ret = -BCH_ERR_fsck_errors_not_fixed; \
|
||||
@ -259,8 +261,8 @@ static void journal_entry_err_msg(struct printbuf *out,
|
||||
true; \
|
||||
})
|
||||
|
||||
#define journal_entry_err_on(cond, c, version, jset, entry, msg, ...) \
|
||||
((cond) ? journal_entry_err(c, version, jset, entry, msg, ##__VA_ARGS__) : false)
|
||||
#define journal_entry_err_on(cond, ...) \
|
||||
((cond) ? journal_entry_err(__VA_ARGS__) : false)
|
||||
|
||||
#define FSCK_DELETED_KEY 5
|
||||
|
||||
@ -277,7 +279,10 @@ static int journal_validate_key(struct bch_fs *c,
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
if (journal_entry_err_on(!k->k.u64s, c, version, jset, entry, "k->u64s 0")) {
|
||||
if (journal_entry_err_on(!k->k.u64s,
|
||||
c, version, jset, entry,
|
||||
journal_entry_bkey_u64s_0,
|
||||
"k->u64s 0")) {
|
||||
entry->u64s = cpu_to_le16((u64 *) k - entry->_data);
|
||||
journal_entry_null_range(vstruct_next(entry), next);
|
||||
return FSCK_DELETED_KEY;
|
||||
@ -286,6 +291,7 @@ static int journal_validate_key(struct bch_fs *c,
|
||||
if (journal_entry_err_on((void *) bkey_next(k) >
|
||||
(void *) vstruct_next(entry),
|
||||
c, version, jset, entry,
|
||||
journal_entry_bkey_past_end,
|
||||
"extends past end of journal entry")) {
|
||||
entry->u64s = cpu_to_le16((u64 *) k - entry->_data);
|
||||
journal_entry_null_range(vstruct_next(entry), next);
|
||||
@ -294,6 +300,7 @@ static int journal_validate_key(struct bch_fs *c,
|
||||
|
||||
if (journal_entry_err_on(k->k.format != KEY_FORMAT_CURRENT,
|
||||
c, version, jset, entry,
|
||||
journal_entry_bkey_bad_format,
|
||||
"bad format %u", k->k.format)) {
|
||||
le16_add_cpu(&entry->u64s, -((u16) k->k.u64s));
|
||||
memmove(k, bkey_next(k), next - (void *) bkey_next(k));
|
||||
@ -317,7 +324,8 @@ static int journal_validate_key(struct bch_fs *c,
|
||||
bch2_bkey_invalid(c, bkey_i_to_s_c(k),
|
||||
__btree_node_type(level, btree_id), write, &buf);
|
||||
|
||||
mustfix_fsck_err(c, "%s", buf.buf);
|
||||
mustfix_fsck_err(c, journal_entry_bkey_invalid,
|
||||
"%s", buf.buf);
|
||||
|
||||
le16_add_cpu(&entry->u64s, -((u16) k->k.u64s));
|
||||
memmove(k, bkey_next(k), next - (void *) bkey_next(k));
|
||||
@ -387,6 +395,7 @@ static int journal_entry_btree_root_validate(struct bch_fs *c,
|
||||
if (journal_entry_err_on(!entry->u64s ||
|
||||
le16_to_cpu(entry->u64s) != k->k.u64s,
|
||||
c, version, jset, entry,
|
||||
journal_entry_btree_root_bad_size,
|
||||
"invalid btree root journal entry: wrong number of keys")) {
|
||||
void *next = vstruct_next(entry);
|
||||
/*
|
||||
@ -436,6 +445,7 @@ static int journal_entry_blacklist_validate(struct bch_fs *c,
|
||||
|
||||
if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 1,
|
||||
c, version, jset, entry,
|
||||
journal_entry_blacklist_bad_size,
|
||||
"invalid journal seq blacklist entry: bad size")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
}
|
||||
@ -463,6 +473,7 @@ static int journal_entry_blacklist_v2_validate(struct bch_fs *c,
|
||||
|
||||
if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 2,
|
||||
c, version, jset, entry,
|
||||
journal_entry_blacklist_v2_bad_size,
|
||||
"invalid journal seq blacklist entry: bad size")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
goto out;
|
||||
@ -473,6 +484,7 @@ static int journal_entry_blacklist_v2_validate(struct bch_fs *c,
|
||||
if (journal_entry_err_on(le64_to_cpu(bl_entry->start) >
|
||||
le64_to_cpu(bl_entry->end),
|
||||
c, version, jset, entry,
|
||||
journal_entry_blacklist_v2_start_past_end,
|
||||
"invalid journal seq blacklist entry: start > end")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
}
|
||||
@ -505,6 +517,7 @@ static int journal_entry_usage_validate(struct bch_fs *c,
|
||||
|
||||
if (journal_entry_err_on(bytes < sizeof(*u),
|
||||
c, version, jset, entry,
|
||||
journal_entry_usage_bad_size,
|
||||
"invalid journal entry usage: bad size")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
return ret;
|
||||
@ -539,6 +552,7 @@ static int journal_entry_data_usage_validate(struct bch_fs *c,
|
||||
if (journal_entry_err_on(bytes < sizeof(*u) ||
|
||||
bytes < sizeof(*u) + u->r.nr_devs,
|
||||
c, version, jset, entry,
|
||||
journal_entry_data_usage_bad_size,
|
||||
"invalid journal entry usage: bad size")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
return ret;
|
||||
@ -570,13 +584,17 @@ static int journal_entry_clock_validate(struct bch_fs *c,
|
||||
int ret = 0;
|
||||
|
||||
if (journal_entry_err_on(bytes != sizeof(*clock),
|
||||
c, version, jset, entry, "bad size")) {
|
||||
c, version, jset, entry,
|
||||
journal_entry_clock_bad_size,
|
||||
"bad size")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (journal_entry_err_on(clock->rw > 1,
|
||||
c, version, jset, entry, "bad rw")) {
|
||||
c, version, jset, entry,
|
||||
journal_entry_clock_bad_rw,
|
||||
"bad rw")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
return ret;
|
||||
}
|
||||
@ -608,7 +626,9 @@ static int journal_entry_dev_usage_validate(struct bch_fs *c,
|
||||
int ret = 0;
|
||||
|
||||
if (journal_entry_err_on(bytes < expected,
|
||||
c, version, jset, entry, "bad size (%u < %u)",
|
||||
c, version, jset, entry,
|
||||
journal_entry_dev_usage_bad_size,
|
||||
"bad size (%u < %u)",
|
||||
bytes, expected)) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
return ret;
|
||||
@ -617,13 +637,17 @@ static int journal_entry_dev_usage_validate(struct bch_fs *c,
|
||||
dev = le32_to_cpu(u->dev);
|
||||
|
||||
if (journal_entry_err_on(!bch2_dev_exists2(c, dev),
|
||||
c, version, jset, entry, "bad dev")) {
|
||||
c, version, jset, entry,
|
||||
journal_entry_dev_usage_bad_dev,
|
||||
"bad dev")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (journal_entry_err_on(u->pad,
|
||||
c, version, jset, entry, "bad pad")) {
|
||||
c, version, jset, entry,
|
||||
journal_entry_dev_usage_bad_pad,
|
||||
"bad pad")) {
|
||||
journal_entry_null_range(entry, vstruct_next(entry));
|
||||
return ret;
|
||||
}
|
||||
@ -738,7 +762,8 @@ static int jset_validate_entries(struct bch_fs *c, struct jset *jset,
|
||||
|
||||
vstruct_for_each(jset, entry) {
|
||||
if (journal_entry_err_on(vstruct_next(entry) > vstruct_last(jset),
|
||||
c, version, jset, entry,
|
||||
c, version, jset, entry,
|
||||
journal_entry_past_jset_end,
|
||||
"journal entry extends past end of jset")) {
|
||||
jset->u64s = cpu_to_le32((u64 *) entry - jset->_data);
|
||||
break;
|
||||
@ -767,6 +792,7 @@ static int jset_validate(struct bch_fs *c,
|
||||
version = le32_to_cpu(jset->version);
|
||||
if (journal_entry_err_on(!bch2_version_compatible(version),
|
||||
c, version, jset, NULL,
|
||||
jset_unsupported_version,
|
||||
"%s sector %llu seq %llu: incompatible journal entry version %u.%u",
|
||||
ca ? ca->name : c->name,
|
||||
sector, le64_to_cpu(jset->seq),
|
||||
@ -777,7 +803,8 @@ static int jset_validate(struct bch_fs *c,
|
||||
}
|
||||
|
||||
if (journal_entry_err_on(!bch2_checksum_type_valid(c, JSET_CSUM_TYPE(jset)),
|
||||
c, version, jset, NULL,
|
||||
c, version, jset, NULL,
|
||||
jset_unknown_csum,
|
||||
"%s sector %llu seq %llu: journal entry with unknown csum type %llu",
|
||||
ca ? ca->name : c->name,
|
||||
sector, le64_to_cpu(jset->seq),
|
||||
@ -788,6 +815,7 @@ static int jset_validate(struct bch_fs *c,
|
||||
if (journal_entry_err_on(!JSET_NO_FLUSH(jset) &&
|
||||
le64_to_cpu(jset->last_seq) > le64_to_cpu(jset->seq),
|
||||
c, version, jset, NULL,
|
||||
jset_last_seq_newer_than_seq,
|
||||
"invalid journal entry: last_seq > seq (%llu > %llu)",
|
||||
le64_to_cpu(jset->last_seq),
|
||||
le64_to_cpu(jset->seq))) {
|
||||
@ -816,7 +844,8 @@ static int jset_validate_early(struct bch_fs *c,
|
||||
|
||||
version = le32_to_cpu(jset->version);
|
||||
if (journal_entry_err_on(!bch2_version_compatible(version),
|
||||
c, version, jset, NULL,
|
||||
c, version, jset, NULL,
|
||||
jset_unsupported_version,
|
||||
"%s sector %llu seq %llu: unknown journal entry version %u.%u",
|
||||
ca ? ca->name : c->name,
|
||||
sector, le64_to_cpu(jset->seq),
|
||||
@ -831,7 +860,8 @@ static int jset_validate_early(struct bch_fs *c,
|
||||
return JOURNAL_ENTRY_REREAD;
|
||||
|
||||
if (journal_entry_err_on(bytes > bucket_sectors_left << 9,
|
||||
c, version, jset, NULL,
|
||||
c, version, jset, NULL,
|
||||
jset_past_bucket_end,
|
||||
"%s sector %llu seq %llu: journal entry too big (%zu bytes)",
|
||||
ca ? ca->name : c->name,
|
||||
sector, le64_to_cpu(jset->seq), bytes))
|
||||
@ -900,7 +930,7 @@ reread:
|
||||
ret = submit_bio_wait(bio);
|
||||
kfree(bio);
|
||||
|
||||
if (bch2_dev_io_err_on(ret, ca,
|
||||
if (bch2_dev_io_err_on(ret, ca, BCH_MEMBER_ERROR_read,
|
||||
"journal read error: sector %llu",
|
||||
offset) ||
|
||||
bch2_meta_read_fault("journal")) {
|
||||
@ -956,7 +986,8 @@ reread:
|
||||
ja->bucket_seq[bucket] = le64_to_cpu(j->seq);
|
||||
|
||||
csum_good = jset_csum_good(c, j);
|
||||
if (!csum_good)
|
||||
if (bch2_dev_io_err_on(!csum_good, ca, BCH_MEMBER_ERROR_checksum,
|
||||
"journal checksum error"))
|
||||
saw_bad = true;
|
||||
|
||||
ret = bch2_encrypt(c, JSET_CSUM_TYPE(j), journal_nonce(j),
|
||||
@ -1172,6 +1203,7 @@ int bch2_journal_read(struct bch_fs *c,
|
||||
|
||||
if (journal_entry_err_on(le64_to_cpu(i->j.last_seq) > le64_to_cpu(i->j.seq),
|
||||
c, le32_to_cpu(i->j.version), &i->j, NULL,
|
||||
jset_last_seq_newer_than_seq,
|
||||
"invalid journal entry: last_seq > seq (%llu > %llu)",
|
||||
le64_to_cpu(i->j.last_seq),
|
||||
le64_to_cpu(i->j.seq)))
|
||||
@ -1188,7 +1220,8 @@ int bch2_journal_read(struct bch_fs *c,
|
||||
}
|
||||
|
||||
if (!*last_seq) {
|
||||
fsck_err(c, "journal read done, but no entries found after dropping non-flushes");
|
||||
fsck_err(c, dirty_but_no_journal_entries_post_drop_nonflushes,
|
||||
"journal read done, but no entries found after dropping non-flushes");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1214,6 +1247,7 @@ int bch2_journal_read(struct bch_fs *c,
|
||||
|
||||
if (bch2_journal_seq_is_blacklisted(c, seq, true)) {
|
||||
fsck_err_on(!JSET_NO_FLUSH(&i->j), c,
|
||||
jset_seq_blacklisted,
|
||||
"found blacklisted journal entry %llu", seq);
|
||||
i->ignore = true;
|
||||
}
|
||||
@ -1254,7 +1288,8 @@ int bch2_journal_read(struct bch_fs *c,
|
||||
bch2_journal_ptrs_to_text(&buf2, c, i);
|
||||
|
||||
missing_end = seq - 1;
|
||||
fsck_err(c, "journal entries %llu-%llu missing! (replaying %llu-%llu)\n"
|
||||
fsck_err(c, journal_entries_missing,
|
||||
"journal entries %llu-%llu missing! (replaying %llu-%llu)\n"
|
||||
" prev at %s\n"
|
||||
" next at %s",
|
||||
missing_start, missing_end,
|
||||
@ -1309,7 +1344,8 @@ int bch2_journal_read(struct bch_fs *c,
|
||||
if (!degraded &&
|
||||
!bch2_replicas_marked(c, &replicas.e) &&
|
||||
(le64_to_cpu(i->j.seq) == *last_seq ||
|
||||
fsck_err(c, "superblock not marked as containing replicas for journal entry %llu\n %s",
|
||||
fsck_err(c, journal_entry_replicas_not_marked,
|
||||
"superblock not marked as containing replicas for journal entry %llu\n %s",
|
||||
le64_to_cpu(i->j.seq), buf.buf))) {
|
||||
ret = bch2_mark_replicas(c, &replicas.e);
|
||||
if (ret)
|
||||
@ -1581,7 +1617,8 @@ static void journal_write_endio(struct bio *bio)
|
||||
struct journal_buf *w = journal_last_unwritten_buf(j);
|
||||
unsigned long flags;
|
||||
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca, "error writing journal entry %llu: %s",
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write,
|
||||
"error writing journal entry %llu: %s",
|
||||
le64_to_cpu(w->data->seq),
|
||||
bch2_blk_status_to_str(bio->bi_status)) ||
|
||||
bch2_meta_write_fault("journal")) {
|
||||
|
@ -10,17 +10,17 @@
|
||||
#include "recovery.h"
|
||||
|
||||
/* KEY_TYPE_lru is obsolete: */
|
||||
int bch2_lru_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_lru_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
if (!lru_pos_time(k.k->p)) {
|
||||
prt_printf(err, "lru entry at time=0");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
int ret = 0;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(!lru_pos_time(k.k->p), c, err,
|
||||
lru_entry_at_time_0,
|
||||
"lru entry at time=0");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_lru_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
@ -95,6 +95,7 @@ static int bch2_check_lru_key(struct btree_trans *trans,
|
||||
int ret;
|
||||
|
||||
if (fsck_err_on(!bch2_dev_bucket_exists(c, alloc_pos), c,
|
||||
lru_entry_to_invalid_bucket,
|
||||
"lru key points to nonexistent device:bucket %llu:%llu",
|
||||
alloc_pos.inode, alloc_pos.offset))
|
||||
return bch2_btree_delete_at(trans, lru_iter, 0);
|
||||
@ -125,7 +126,8 @@ static int bch2_check_lru_key(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (c->opts.reconstruct_alloc ||
|
||||
fsck_err(c, "incorrect lru entry: lru %s time %llu\n"
|
||||
fsck_err(c, lru_entry_bad,
|
||||
"incorrect lru entry: lru %s time %llu\n"
|
||||
" %s\n"
|
||||
" for %s",
|
||||
bch2_lru_types[type],
|
||||
|
@ -48,7 +48,7 @@ static inline enum bch_lru_type lru_type(struct bkey_s_c l)
|
||||
return BCH_LRU_read;
|
||||
}
|
||||
|
||||
int bch2_lru_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_lru_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_lru_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
|
@ -12,11 +12,6 @@
|
||||
|
||||
#define x(t, n, ...) [n] = #t,
|
||||
|
||||
const char * const bch2_iops_measurements[] = {
|
||||
BCH_IOPS_MEASUREMENTS()
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const bch2_error_actions[] = {
|
||||
BCH_ERROR_ACTIONS()
|
||||
NULL
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
struct bch_fs;
|
||||
|
||||
extern const char * const bch2_iops_measurements[];
|
||||
extern const char * const bch2_error_actions[];
|
||||
extern const char * const bch2_fsck_fix_opts[];
|
||||
extern const char * const bch2_version_upgrade_opts[];
|
||||
|
@ -59,17 +59,18 @@ const struct bch_sb_field_ops bch_sb_field_ops_quota = {
|
||||
.to_text = bch2_sb_quota_to_text,
|
||||
};
|
||||
|
||||
int bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_quota_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
if (k.k->p.inode >= QTYP_NR) {
|
||||
prt_printf(err, "invalid quota type (%llu >= %u)",
|
||||
k.k->p.inode, QTYP_NR);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(k.k->p.inode >= QTYP_NR, c, err,
|
||||
quota_type_invalid,
|
||||
"invalid quota type (%llu >= %u)",
|
||||
k.k->p.inode, QTYP_NR);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
|
@ -8,7 +8,7 @@
|
||||
enum bkey_invalid_flags;
|
||||
extern const struct bch_sb_field_ops bch_sb_field_ops_quota;
|
||||
|
||||
int bch2_quota_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_quota_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_quota_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
|
@ -354,7 +354,8 @@ static int do_rebalance(struct moving_context *ctxt)
|
||||
rebalance_wait(c);
|
||||
}
|
||||
|
||||
bch_err_fn(c, ret);
|
||||
if (!bch2_err_matches(ret, EROFS))
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -365,8 +365,10 @@ static int read_btree_roots(struct bch_fs *c)
|
||||
}
|
||||
|
||||
if (r->error) {
|
||||
__fsck_err(c, btree_id_is_alloc(i)
|
||||
__fsck_err(c,
|
||||
btree_id_is_alloc(i)
|
||||
? FSCK_CAN_IGNORE : 0,
|
||||
btree_root_bkey_invalid,
|
||||
"invalid btree root %s",
|
||||
bch2_btree_id_str(i));
|
||||
if (i == BTREE_ID_alloc)
|
||||
@ -376,6 +378,7 @@ static int read_btree_roots(struct bch_fs *c)
|
||||
ret = bch2_btree_root_read(c, i, &r->key, r->level);
|
||||
if (ret) {
|
||||
fsck_err(c,
|
||||
btree_root_read_error,
|
||||
"error reading btree root %s",
|
||||
bch2_btree_id_str(i));
|
||||
if (btree_id_is_alloc(i))
|
||||
@ -714,6 +717,7 @@ int bch2_fs_recovery(struct bch_fs *c)
|
||||
if (mustfix_fsck_err_on(c->sb.clean &&
|
||||
last_journal_entry &&
|
||||
!journal_entry_empty(last_journal_entry), c,
|
||||
clean_but_journal_not_empty,
|
||||
"filesystem marked clean but journal not empty")) {
|
||||
c->sb.compat &= ~(1ULL << BCH_COMPAT_alloc_info);
|
||||
SET_BCH_SB_CLEAN(c->disk_sb.sb, false);
|
||||
@ -721,7 +725,9 @@ int bch2_fs_recovery(struct bch_fs *c)
|
||||
}
|
||||
|
||||
if (!last_journal_entry) {
|
||||
fsck_err_on(!c->sb.clean, c, "no journal entries found");
|
||||
fsck_err_on(!c->sb.clean, c,
|
||||
dirty_but_no_journal_entries,
|
||||
"no journal entries found");
|
||||
if (clean)
|
||||
goto use_clean;
|
||||
|
||||
|
@ -28,7 +28,7 @@ static inline unsigned bkey_type_to_indirect(const struct bkey *k)
|
||||
|
||||
/* reflink pointers */
|
||||
|
||||
int bch2_reflink_p_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_reflink_p_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
@ -75,7 +75,7 @@ bool bch2_reflink_p_merge(struct bch_fs *c, struct bkey_s _l, struct bkey_s_c _r
|
||||
|
||||
/* indirect extents */
|
||||
|
||||
int bch2_reflink_v_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_reflink_v_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
@ -126,7 +126,7 @@ int bch2_trans_mark_reflink_v(struct btree_trans *trans,
|
||||
|
||||
/* indirect inline data */
|
||||
|
||||
int bch2_indirect_inline_data_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_indirect_inline_data_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
enum bkey_invalid_flags;
|
||||
|
||||
int bch2_reflink_p_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_reflink_p_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_reflink_p_to_text(struct printbuf *, struct bch_fs *,
|
||||
struct bkey_s_c);
|
||||
@ -19,7 +19,7 @@ bool bch2_reflink_p_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
|
||||
.min_val_size = 16, \
|
||||
})
|
||||
|
||||
int bch2_reflink_v_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_reflink_v_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_reflink_v_to_text(struct printbuf *, struct bch_fs *,
|
||||
struct bkey_s_c);
|
||||
@ -35,7 +35,7 @@ int bch2_trans_mark_reflink_v(struct btree_trans *, enum btree_id, unsigned,
|
||||
.min_val_size = 8, \
|
||||
})
|
||||
|
||||
int bch2_indirect_inline_data_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_indirect_inline_data_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_indirect_inline_data_to_text(struct printbuf *,
|
||||
struct bch_fs *, struct bkey_s_c);
|
||||
|
@ -462,18 +462,13 @@ int bch2_replicas_gc_end(struct bch_fs *c, int ret)
|
||||
{
|
||||
lockdep_assert_held(&c->replicas_gc_lock);
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
mutex_lock(&c->sb_lock);
|
||||
percpu_down_write(&c->mark_lock);
|
||||
|
||||
ret = bch2_cpu_replicas_to_sb_replicas(c, &c->replicas_gc);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = ret ?:
|
||||
bch2_cpu_replicas_to_sb_replicas(c, &c->replicas_gc) ?:
|
||||
replicas_table_update(c, &c->replicas_gc);
|
||||
|
||||
ret = replicas_table_update(c, &c->replicas_gc);
|
||||
err:
|
||||
kfree(c->replicas_gc.entries);
|
||||
c->replicas_gc.entries = NULL;
|
||||
|
||||
@ -579,12 +574,9 @@ retry:
|
||||
|
||||
bch2_cpu_replicas_sort(&new);
|
||||
|
||||
ret = bch2_cpu_replicas_to_sb_replicas(c, &new);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bch2_cpu_replicas_to_sb_replicas(c, &new) ?:
|
||||
replicas_table_update(c, &new);
|
||||
|
||||
ret = replicas_table_update(c, &new);
|
||||
err:
|
||||
kfree(new.entries);
|
||||
|
||||
percpu_up_write(&c->mark_lock);
|
||||
|
@ -82,6 +82,7 @@ int bch2_verify_superblock_clean(struct bch_fs *c,
|
||||
int ret = 0;
|
||||
|
||||
if (mustfix_fsck_err_on(j->seq != clean->journal_seq, c,
|
||||
sb_clean_journal_seq_mismatch,
|
||||
"superblock journal seq (%llu) doesn't match journal (%llu) after clean shutdown",
|
||||
le64_to_cpu(clean->journal_seq),
|
||||
le64_to_cpu(j->seq))) {
|
||||
@ -119,6 +120,7 @@ int bch2_verify_superblock_clean(struct bch_fs *c,
|
||||
k1->k.u64s != k2->k.u64s ||
|
||||
memcmp(k1, k2, bkey_bytes(&k1->k)) ||
|
||||
l1 != l2, c,
|
||||
sb_clean_btree_root_mismatch,
|
||||
"superblock btree root %u doesn't match journal after clean shutdown\n"
|
||||
"sb: l=%u %s\n"
|
||||
"journal: l=%u %s\n", i,
|
||||
@ -140,6 +142,7 @@ struct bch_sb_field_clean *bch2_read_superblock_clean(struct bch_fs *c)
|
||||
sb_clean = bch2_sb_field_get(c->disk_sb.sb, clean);
|
||||
|
||||
if (fsck_err_on(!sb_clean, c,
|
||||
sb_clean_missing,
|
||||
"superblock marked clean but clean section not present")) {
|
||||
SET_BCH_SB_CLEAN(c->disk_sb.sb, false);
|
||||
c->sb.clean = false;
|
||||
|
175
libbcachefs/sb-errors.c
Normal file
175
libbcachefs/sb-errors.c
Normal file
@ -0,0 +1,175 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "bcachefs.h"
|
||||
#include "sb-errors.h"
|
||||
#include "super-io.h"
|
||||
|
||||
static const char * const bch2_sb_error_strs[] = {
|
||||
#define x(t, n, ...) [n] = #t,
|
||||
BCH_SB_ERRS()
|
||||
NULL
|
||||
};
|
||||
|
||||
static void bch2_sb_error_id_to_text(struct printbuf *out, enum bch_sb_error_id id)
|
||||
{
|
||||
if (id < BCH_SB_ERR_MAX)
|
||||
prt_str(out, bch2_sb_error_strs[id]);
|
||||
else
|
||||
prt_printf(out, "(unknown error %u)", id);
|
||||
}
|
||||
|
||||
static inline unsigned bch2_sb_field_errors_nr_entries(struct bch_sb_field_errors *e)
|
||||
{
|
||||
return e
|
||||
? (bch2_sb_field_bytes(&e->field) - sizeof(*e)) / sizeof(e->entries[0])
|
||||
: 0;
|
||||
}
|
||||
|
||||
static inline unsigned bch2_sb_field_errors_u64s(unsigned nr)
|
||||
{
|
||||
return (sizeof(struct bch_sb_field_errors) +
|
||||
sizeof(struct bch_sb_field_error_entry) * nr) / sizeof(u64);
|
||||
}
|
||||
|
||||
static int bch2_sb_errors_validate(struct bch_sb *sb, struct bch_sb_field *f,
|
||||
struct printbuf *err)
|
||||
{
|
||||
struct bch_sb_field_errors *e = field_to_type(f, errors);
|
||||
unsigned i, nr = bch2_sb_field_errors_nr_entries(e);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
if (!BCH_SB_ERROR_ENTRY_NR(&e->entries[i])) {
|
||||
prt_printf(err, "entry with count 0 (id ");
|
||||
bch2_sb_error_id_to_text(err, BCH_SB_ERROR_ENTRY_ID(&e->entries[i]));
|
||||
prt_printf(err, ")");
|
||||
return -BCH_ERR_invalid_sb_errors;
|
||||
}
|
||||
|
||||
if (i + 1 < nr &&
|
||||
BCH_SB_ERROR_ENTRY_ID(&e->entries[i]) >=
|
||||
BCH_SB_ERROR_ENTRY_ID(&e->entries[i + 1])) {
|
||||
prt_printf(err, "entries out of order");
|
||||
return -BCH_ERR_invalid_sb_errors;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bch2_sb_errors_to_text(struct printbuf *out, struct bch_sb *sb,
|
||||
struct bch_sb_field *f)
|
||||
{
|
||||
struct bch_sb_field_errors *e = field_to_type(f, errors);
|
||||
unsigned i, nr = bch2_sb_field_errors_nr_entries(e);
|
||||
u64 now = ktime_get_real_seconds();
|
||||
|
||||
if (out->nr_tabstops <= 1)
|
||||
printbuf_tabstop_push(out, 16);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
bch2_sb_error_id_to_text(out, BCH_SB_ERROR_ENTRY_ID(&e->entries[i]));
|
||||
prt_tab(out);
|
||||
prt_u64(out, BCH_SB_ERROR_ENTRY_NR(&e->entries[i]));
|
||||
prt_tab(out);
|
||||
bch2_pr_time_units(out, (now - le64_to_cpu(e->entries[i].last_error_time)) *
|
||||
NSEC_PER_SEC);
|
||||
prt_str(out, " ago");
|
||||
prt_newline(out);
|
||||
}
|
||||
}
|
||||
|
||||
const struct bch_sb_field_ops bch_sb_field_ops_errors = {
|
||||
.validate = bch2_sb_errors_validate,
|
||||
.to_text = bch2_sb_errors_to_text,
|
||||
};
|
||||
|
||||
void bch2_sb_error_count(struct bch_fs *c, enum bch_sb_error_id err)
|
||||
{
|
||||
bch_sb_errors_cpu *e = &c->fsck_error_counts;
|
||||
struct bch_sb_error_entry_cpu n = {
|
||||
.id = err,
|
||||
.nr = 1,
|
||||
.last_error_time = ktime_get_real_seconds()
|
||||
};
|
||||
unsigned i;
|
||||
|
||||
mutex_lock(&c->fsck_error_counts_lock);
|
||||
for (i = 0; i < e->nr; i++) {
|
||||
if (err == e->data[i].id) {
|
||||
e->data[i].nr++;
|
||||
e->data[i].last_error_time = n.last_error_time;
|
||||
goto out;
|
||||
}
|
||||
if (err < e->data[i].id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (darray_make_room(e, 1))
|
||||
goto out;
|
||||
|
||||
darray_insert_item(e, i, n);
|
||||
out:
|
||||
mutex_unlock(&c->fsck_error_counts_lock);
|
||||
}
|
||||
|
||||
void bch2_sb_errors_from_cpu(struct bch_fs *c)
|
||||
{
|
||||
bch_sb_errors_cpu *src = &c->fsck_error_counts;
|
||||
struct bch_sb_field_errors *dst =
|
||||
bch2_sb_field_resize(&c->disk_sb, errors,
|
||||
bch2_sb_field_errors_u64s(src->nr));
|
||||
unsigned i;
|
||||
|
||||
if (!dst)
|
||||
return;
|
||||
|
||||
for (i = 0; i < src->nr; i++) {
|
||||
SET_BCH_SB_ERROR_ENTRY_ID(&dst->entries[i], src->data[i].id);
|
||||
SET_BCH_SB_ERROR_ENTRY_NR(&dst->entries[i], src->data[i].nr);
|
||||
dst->entries[i].last_error_time = cpu_to_le64(src->data[i].last_error_time);
|
||||
}
|
||||
}
|
||||
|
||||
static int bch2_sb_errors_to_cpu(struct bch_fs *c)
|
||||
{
|
||||
struct bch_sb_field_errors *src = bch2_sb_field_get(c->disk_sb.sb, errors);
|
||||
bch_sb_errors_cpu *dst = &c->fsck_error_counts;
|
||||
unsigned i, nr = bch2_sb_field_errors_nr_entries(src);
|
||||
int ret;
|
||||
|
||||
if (!nr)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&c->fsck_error_counts_lock);
|
||||
ret = darray_make_room(dst, nr);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dst->nr = nr;
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
dst->data[i].id = BCH_SB_ERROR_ENTRY_ID(&src->entries[i]);
|
||||
dst->data[i].nr = BCH_SB_ERROR_ENTRY_NR(&src->entries[i]);
|
||||
dst->data[i].last_error_time = le64_to_cpu(src->entries[i].last_error_time);
|
||||
}
|
||||
err:
|
||||
mutex_unlock(&c->fsck_error_counts_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_fs_sb_errors_exit(struct bch_fs *c)
|
||||
{
|
||||
darray_exit(&c->fsck_error_counts);
|
||||
}
|
||||
|
||||
void bch2_fs_sb_errors_init_early(struct bch_fs *c)
|
||||
{
|
||||
mutex_init(&c->fsck_error_counts_lock);
|
||||
darray_init(&c->fsck_error_counts);
|
||||
}
|
||||
|
||||
int bch2_fs_sb_errors_init(struct bch_fs *c)
|
||||
{
|
||||
return bch2_sb_errors_to_cpu(c);
|
||||
}
|
270
libbcachefs/sb-errors.h
Normal file
270
libbcachefs/sb-errors.h
Normal file
@ -0,0 +1,270 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BCACHEFS_SB_ERRORS_H
|
||||
#define _BCACHEFS_SB_ERRORS_H
|
||||
|
||||
#include "sb-errors_types.h"
|
||||
|
||||
#define BCH_SB_ERRS() \
|
||||
x(clean_but_journal_not_empty, 0) \
|
||||
x(dirty_but_no_journal_entries, 1) \
|
||||
x(dirty_but_no_journal_entries_post_drop_nonflushes, 2) \
|
||||
x(sb_clean_journal_seq_mismatch, 3) \
|
||||
x(sb_clean_btree_root_mismatch, 4) \
|
||||
x(sb_clean_missing, 5) \
|
||||
x(jset_unsupported_version, 6) \
|
||||
x(jset_unknown_csum, 7) \
|
||||
x(jset_last_seq_newer_than_seq, 8) \
|
||||
x(jset_past_bucket_end, 9) \
|
||||
x(jset_seq_blacklisted, 10) \
|
||||
x(journal_entries_missing, 11) \
|
||||
x(journal_entry_replicas_not_marked, 12) \
|
||||
x(journal_entry_past_jset_end, 13) \
|
||||
x(journal_entry_replicas_data_mismatch, 14) \
|
||||
x(journal_entry_bkey_u64s_0, 15) \
|
||||
x(journal_entry_bkey_past_end, 16) \
|
||||
x(journal_entry_bkey_bad_format, 17) \
|
||||
x(journal_entry_bkey_invalid, 18) \
|
||||
x(journal_entry_btree_root_bad_size, 19) \
|
||||
x(journal_entry_blacklist_bad_size, 20) \
|
||||
x(journal_entry_blacklist_v2_bad_size, 21) \
|
||||
x(journal_entry_blacklist_v2_start_past_end, 22) \
|
||||
x(journal_entry_usage_bad_size, 23) \
|
||||
x(journal_entry_data_usage_bad_size, 24) \
|
||||
x(journal_entry_clock_bad_size, 25) \
|
||||
x(journal_entry_clock_bad_rw, 26) \
|
||||
x(journal_entry_dev_usage_bad_size, 27) \
|
||||
x(journal_entry_dev_usage_bad_dev, 28) \
|
||||
x(journal_entry_dev_usage_bad_pad, 29) \
|
||||
x(btree_node_unreadable, 30) \
|
||||
x(btree_node_fault_injected, 31) \
|
||||
x(btree_node_bad_magic, 32) \
|
||||
x(btree_node_bad_seq, 33) \
|
||||
x(btree_node_unsupported_version, 34) \
|
||||
x(btree_node_bset_older_than_sb_min, 35) \
|
||||
x(btree_node_bset_newer_than_sb, 36) \
|
||||
x(btree_node_data_missing, 37) \
|
||||
x(btree_node_bset_after_end, 38) \
|
||||
x(btree_node_replicas_sectors_written_mismatch, 39) \
|
||||
x(btree_node_replicas_data_mismatch, 40) \
|
||||
x(bset_unknown_csum, 41) \
|
||||
x(bset_bad_csum, 42) \
|
||||
x(bset_past_end_of_btree_node, 43) \
|
||||
x(bset_wrong_sector_offset, 44) \
|
||||
x(bset_empty, 45) \
|
||||
x(bset_bad_seq, 46) \
|
||||
x(bset_blacklisted_journal_seq, 47) \
|
||||
x(first_bset_blacklisted_journal_seq, 48) \
|
||||
x(btree_node_bad_btree, 49) \
|
||||
x(btree_node_bad_level, 50) \
|
||||
x(btree_node_bad_min_key, 51) \
|
||||
x(btree_node_bad_max_key, 52) \
|
||||
x(btree_node_bad_format, 53) \
|
||||
x(btree_node_bkey_past_bset_end, 54) \
|
||||
x(btree_node_bkey_bad_format, 55) \
|
||||
x(btree_node_bad_bkey, 56) \
|
||||
x(btree_node_bkey_out_of_order, 57) \
|
||||
x(btree_root_bkey_invalid, 58) \
|
||||
x(btree_root_read_error, 59) \
|
||||
x(btree_root_bad_min_key, 50) \
|
||||
x(btree_root_bad_max_key, 61) \
|
||||
x(btree_node_read_error, 62) \
|
||||
x(btree_node_topology_bad_min_key, 63) \
|
||||
x(btree_node_topology_bad_max_key, 64) \
|
||||
x(btree_node_topology_overwritten_by_prev_node, 65) \
|
||||
x(btree_node_topology_overwritten_by_next_node, 66) \
|
||||
x(btree_node_topology_interior_node_empty, 67) \
|
||||
x(fs_usage_hidden_wrong, 68) \
|
||||
x(fs_usage_btree_wrong, 69) \
|
||||
x(fs_usage_data_wrong, 70) \
|
||||
x(fs_usage_cached_wrong, 71) \
|
||||
x(fs_usage_reserved_wrong, 72) \
|
||||
x(fs_usage_persistent_reserved_wrong, 73) \
|
||||
x(fs_usage_nr_inodes_wrong, 74) \
|
||||
x(fs_usage_replicas_wrong, 75) \
|
||||
x(dev_usage_buckets_wrong, 76) \
|
||||
x(dev_usage_sectors_wrong, 77) \
|
||||
x(dev_usage_fragmented_wrong, 78) \
|
||||
x(dev_usage_buckets_ec_wrong, 79) \
|
||||
x(bkey_version_in_future, 80) \
|
||||
x(bkey_u64s_too_small, 81) \
|
||||
x(bkey_invalid_type_for_btree, 82) \
|
||||
x(bkey_extent_size_zero, 83) \
|
||||
x(bkey_extent_size_greater_than_offset, 84) \
|
||||
x(bkey_size_nonzero, 85) \
|
||||
x(bkey_snapshot_nonzero, 86) \
|
||||
x(bkey_snapshot_zero, 87) \
|
||||
x(bkey_at_pos_max, 88) \
|
||||
x(bkey_before_start_of_btree_node, 89) \
|
||||
x(bkey_after_end_of_btree_node, 90) \
|
||||
x(bkey_val_size_nonzero, 91) \
|
||||
x(bkey_val_size_too_small, 92) \
|
||||
x(alloc_v1_val_size_bad, 93) \
|
||||
x(alloc_v2_unpack_error, 94) \
|
||||
x(alloc_v3_unpack_error, 95) \
|
||||
x(alloc_v4_val_size_bad, 96) \
|
||||
x(alloc_v4_backpointers_start_bad, 97) \
|
||||
x(alloc_key_data_type_bad, 98) \
|
||||
x(alloc_key_empty_but_have_data, 99) \
|
||||
x(alloc_key_dirty_sectors_0, 100) \
|
||||
x(alloc_key_data_type_inconsistency, 101) \
|
||||
x(alloc_key_to_missing_dev_bucket, 102) \
|
||||
x(alloc_key_cached_inconsistency, 103) \
|
||||
x(alloc_key_cached_but_read_time_zero, 104) \
|
||||
x(alloc_key_to_missing_lru_entry, 105) \
|
||||
x(alloc_key_data_type_wrong, 106) \
|
||||
x(alloc_key_gen_wrong, 107) \
|
||||
x(alloc_key_dirty_sectors_wrong, 108) \
|
||||
x(alloc_key_cached_sectors_wrong, 109) \
|
||||
x(alloc_key_stripe_wrong, 110) \
|
||||
x(alloc_key_stripe_redundancy_wrong, 111) \
|
||||
x(bucket_sector_count_overflow, 112) \
|
||||
x(bucket_metadata_type_mismatch, 113) \
|
||||
x(need_discard_key_wrong, 114) \
|
||||
x(freespace_key_wrong, 115) \
|
||||
x(freespace_hole_missing, 116) \
|
||||
x(bucket_gens_val_size_bad, 117) \
|
||||
x(bucket_gens_key_wrong, 118) \
|
||||
x(bucket_gens_hole_wrong, 119) \
|
||||
x(bucket_gens_to_invalid_dev, 120) \
|
||||
x(bucket_gens_to_invalid_buckets, 121) \
|
||||
x(bucket_gens_nonzero_for_invalid_buckets, 122) \
|
||||
x(need_discard_freespace_key_to_invalid_dev_bucket, 123) \
|
||||
x(need_discard_freespace_key_bad, 124) \
|
||||
x(backpointer_pos_wrong, 125) \
|
||||
x(backpointer_to_missing_device, 126) \
|
||||
x(backpointer_to_missing_alloc, 127) \
|
||||
x(backpointer_to_missing_ptr, 128) \
|
||||
x(lru_entry_at_time_0, 129) \
|
||||
x(lru_entry_to_invalid_bucket, 130) \
|
||||
x(lru_entry_bad, 131) \
|
||||
x(btree_ptr_val_too_big, 132) \
|
||||
x(btree_ptr_v2_val_too_big, 133) \
|
||||
x(btree_ptr_has_non_ptr, 134) \
|
||||
x(extent_ptrs_invalid_entry, 135) \
|
||||
x(extent_ptrs_no_ptrs, 136) \
|
||||
x(extent_ptrs_too_many_ptrs, 137) \
|
||||
x(extent_ptrs_redundant_crc, 138) \
|
||||
x(extent_ptrs_redundant_stripe, 139) \
|
||||
x(extent_ptrs_unwritten, 140) \
|
||||
x(extent_ptrs_written_and_unwritten, 141) \
|
||||
x(ptr_to_invalid_device, 142) \
|
||||
x(ptr_to_duplicate_device, 143) \
|
||||
x(ptr_after_last_bucket, 144) \
|
||||
x(ptr_before_first_bucket, 145) \
|
||||
x(ptr_spans_multiple_buckets, 146) \
|
||||
x(ptr_to_missing_backpointer, 147) \
|
||||
x(ptr_to_missing_alloc_key, 148) \
|
||||
x(ptr_to_missing_replicas_entry, 149) \
|
||||
x(ptr_to_missing_stripe, 150) \
|
||||
x(ptr_to_incorrect_stripe, 151) \
|
||||
x(ptr_gen_newer_than_bucket_gen, 152) \
|
||||
x(ptr_too_stale, 153) \
|
||||
x(stale_dirty_ptr, 154) \
|
||||
x(ptr_bucket_data_type_mismatch, 155) \
|
||||
x(ptr_cached_and_erasure_coded, 156) \
|
||||
x(ptr_crc_uncompressed_size_too_small, 157) \
|
||||
x(ptr_crc_csum_type_unknown, 158) \
|
||||
x(ptr_crc_compression_type_unknown, 159) \
|
||||
x(ptr_crc_redundant, 160) \
|
||||
x(ptr_crc_uncompressed_size_too_big, 161) \
|
||||
x(ptr_crc_nonce_mismatch, 162) \
|
||||
x(ptr_stripe_redundant, 163) \
|
||||
x(reservation_key_nr_replicas_invalid, 164) \
|
||||
x(reflink_v_refcount_wrong, 165) \
|
||||
x(reflink_p_to_missing_reflink_v, 166) \
|
||||
x(stripe_pos_bad, 167) \
|
||||
x(stripe_val_size_bad, 168) \
|
||||
x(stripe_sector_count_wrong, 169) \
|
||||
x(snapshot_tree_pos_bad, 170) \
|
||||
x(snapshot_tree_to_missing_snapshot, 171) \
|
||||
x(snapshot_tree_to_missing_subvol, 172) \
|
||||
x(snapshot_tree_to_wrong_subvol, 173) \
|
||||
x(snapshot_tree_to_snapshot_subvol, 174) \
|
||||
x(snapshot_pos_bad, 175) \
|
||||
x(snapshot_parent_bad, 176) \
|
||||
x(snapshot_children_not_normalized, 177) \
|
||||
x(snapshot_child_duplicate, 178) \
|
||||
x(snapshot_child_bad, 179) \
|
||||
x(snapshot_skiplist_not_normalized, 180) \
|
||||
x(snapshot_skiplist_bad, 181) \
|
||||
x(snapshot_should_not_have_subvol, 182) \
|
||||
x(snapshot_to_bad_snapshot_tree, 183) \
|
||||
x(snapshot_bad_depth, 184) \
|
||||
x(snapshot_bad_skiplist, 185) \
|
||||
x(subvol_pos_bad, 186) \
|
||||
x(subvol_not_master_and_not_snapshot, 187) \
|
||||
x(subvol_to_missing_root, 188) \
|
||||
x(subvol_root_wrong_bi_subvol, 189) \
|
||||
x(bkey_in_missing_snapshot, 190) \
|
||||
x(inode_pos_inode_nonzero, 191) \
|
||||
x(inode_pos_blockdev_range, 192) \
|
||||
x(inode_unpack_error, 193) \
|
||||
x(inode_str_hash_invalid, 194) \
|
||||
x(inode_v3_fields_start_bad, 195) \
|
||||
x(inode_snapshot_mismatch, 196) \
|
||||
x(inode_unlinked_but_clean, 197) \
|
||||
x(inode_unlinked_but_nlink_nonzero, 198) \
|
||||
x(inode_checksum_type_invalid, 199) \
|
||||
x(inode_compression_type_invalid, 200) \
|
||||
x(inode_subvol_root_but_not_dir, 201) \
|
||||
x(inode_i_size_dirty_but_clean, 202) \
|
||||
x(inode_i_sectors_dirty_but_clean, 203) \
|
||||
x(inode_i_sectors_wrong, 204) \
|
||||
x(inode_dir_wrong_nlink, 205) \
|
||||
x(inode_dir_multiple_links, 206) \
|
||||
x(inode_multiple_links_but_nlink_0, 207) \
|
||||
x(inode_wrong_backpointer, 208) \
|
||||
x(inode_wrong_nlink, 209) \
|
||||
x(inode_unreachable, 210) \
|
||||
x(deleted_inode_but_clean, 211) \
|
||||
x(deleted_inode_missing, 212) \
|
||||
x(deleted_inode_is_dir, 213) \
|
||||
x(deleted_inode_not_unlinked, 214) \
|
||||
x(extent_overlapping, 215) \
|
||||
x(extent_in_missing_inode, 216) \
|
||||
x(extent_in_non_reg_inode, 217) \
|
||||
x(extent_past_end_of_inode, 218) \
|
||||
x(dirent_empty_name, 219) \
|
||||
x(dirent_val_too_big, 220) \
|
||||
x(dirent_name_too_long, 221) \
|
||||
x(dirent_name_embedded_nul, 222) \
|
||||
x(dirent_name_dot_or_dotdot, 223) \
|
||||
x(dirent_name_has_slash, 224) \
|
||||
x(dirent_d_type_wrong, 225) \
|
||||
x(dirent_d_parent_subvol_wrong, 226) \
|
||||
x(dirent_in_missing_dir_inode, 227) \
|
||||
x(dirent_in_non_dir_inode, 228) \
|
||||
x(dirent_to_missing_inode, 229) \
|
||||
x(dirent_to_missing_subvol, 230) \
|
||||
x(dirent_to_itself, 231) \
|
||||
x(quota_type_invalid, 232) \
|
||||
x(xattr_val_size_too_small, 233) \
|
||||
x(xattr_val_size_too_big, 234) \
|
||||
x(xattr_invalid_type, 235) \
|
||||
x(xattr_name_invalid_chars, 236) \
|
||||
x(xattr_in_missing_inode, 237) \
|
||||
x(root_subvol_missing, 238) \
|
||||
x(root_dir_missing, 239) \
|
||||
x(root_inode_not_dir, 240) \
|
||||
x(dir_loop, 241) \
|
||||
x(hash_table_key_duplicate, 242) \
|
||||
x(hash_table_key_wrong_offset, 243)
|
||||
|
||||
enum bch_sb_error_id {
|
||||
#define x(t, n) BCH_FSCK_ERR_##t = n,
|
||||
BCH_SB_ERRS()
|
||||
#undef x
|
||||
BCH_SB_ERR_MAX
|
||||
};
|
||||
|
||||
extern const struct bch_sb_field_ops bch_sb_field_ops_errors;
|
||||
|
||||
void bch2_sb_error_count(struct bch_fs *, enum bch_sb_error_id);
|
||||
|
||||
void bch2_sb_errors_from_cpu(struct bch_fs *);
|
||||
|
||||
void bch2_fs_sb_errors_exit(struct bch_fs *);
|
||||
void bch2_fs_sb_errors_init_early(struct bch_fs *);
|
||||
int bch2_fs_sb_errors_init(struct bch_fs *);
|
||||
|
||||
#endif /* _BCACHEFS_SB_ERRORS_H */
|
16
libbcachefs/sb-errors_types.h
Normal file
16
libbcachefs/sb-errors_types.h
Normal file
@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BCACHEFS_SB_ERRORS_TYPES_H
|
||||
#define _BCACHEFS_SB_ERRORS_TYPES_H
|
||||
|
||||
#include "darray.h"
|
||||
|
||||
struct bch_sb_error_entry_cpu {
|
||||
u64 id:16,
|
||||
nr:48;
|
||||
u64 last_error_time;
|
||||
};
|
||||
|
||||
typedef DARRAY(struct bch_sb_error_entry_cpu) bch_sb_errors_cpu;
|
||||
|
||||
#endif /* _BCACHEFS_SB_ERRORS_TYPES_H */
|
||||
|
@ -7,6 +7,18 @@
|
||||
#include "sb-members.h"
|
||||
#include "super-io.h"
|
||||
|
||||
#define x(t, n, ...) [n] = #t,
|
||||
static const char * const bch2_iops_measurements[] = {
|
||||
BCH_IOPS_MEASUREMENTS()
|
||||
NULL
|
||||
};
|
||||
|
||||
char * const bch2_member_error_strs[] = {
|
||||
BCH_MEMBER_ERROR_TYPES()
|
||||
NULL
|
||||
};
|
||||
#undef x
|
||||
|
||||
/* Code for bch_sb_field_members_v1: */
|
||||
|
||||
static struct bch_member *members_v2_get_mut(struct bch_sb_field_members_v2 *mi, int i)
|
||||
@ -36,7 +48,8 @@ static struct bch_member members_v1_get(struct bch_sb_field_members_v1 *mi, int
|
||||
{
|
||||
struct bch_member ret, *p = members_v1_get_mut(mi, i);
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
memcpy(&ret, p, min_t(size_t, sizeof(struct bch_member), sizeof(ret))); return ret;
|
||||
memcpy(&ret, p, min_t(size_t, BCH_MEMBER_V1_BYTES, sizeof(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct bch_member bch2_sb_member_get(struct bch_sb *sb, int i)
|
||||
@ -71,7 +84,7 @@ static int sb_members_v2_resize_entries(struct bch_fs *c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bch2_members_v2_init(struct bch_fs *c)
|
||||
int bch2_sb_members_v2_init(struct bch_fs *c)
|
||||
{
|
||||
struct bch_sb_field_members_v1 *mi1;
|
||||
struct bch_sb_field_members_v2 *mi2;
|
||||
@ -91,7 +104,7 @@ int bch2_members_v2_init(struct bch_fs *c)
|
||||
return sb_members_v2_resize_entries(c);
|
||||
}
|
||||
|
||||
int bch_members_cpy_v2_v1(struct bch_sb_handle *disk_sb)
|
||||
int bch2_sb_members_cpy_v2_v1(struct bch_sb_handle *disk_sb)
|
||||
{
|
||||
struct bch_sb_field_members_v1 *mi1;
|
||||
struct bch_sb_field_members_v2 *mi2;
|
||||
@ -155,7 +168,6 @@ static void member_to_text(struct printbuf *out,
|
||||
u64 bucket_size = le16_to_cpu(m.bucket_size);
|
||||
u64 device_size = le64_to_cpu(m.nbuckets) * bucket_size;
|
||||
|
||||
|
||||
prt_printf(out, "Device:");
|
||||
prt_tab(out);
|
||||
prt_printf(out, "%u", i);
|
||||
@ -163,6 +175,21 @@ static void member_to_text(struct printbuf *out,
|
||||
|
||||
printbuf_indent_add(out, 2);
|
||||
|
||||
prt_printf(out, "Label:");
|
||||
prt_tab(out);
|
||||
if (BCH_MEMBER_GROUP(&m)) {
|
||||
unsigned idx = BCH_MEMBER_GROUP(&m) - 1;
|
||||
|
||||
if (idx < disk_groups_nr(gi))
|
||||
prt_printf(out, "%s (%u)",
|
||||
gi->entries[idx].label, idx);
|
||||
else
|
||||
prt_printf(out, "(bad disk labels section)");
|
||||
} else {
|
||||
prt_printf(out, "(none)");
|
||||
}
|
||||
prt_newline(out);
|
||||
|
||||
prt_printf(out, "UUID:");
|
||||
prt_tab(out);
|
||||
pr_uuid(out, m.uuid.b);
|
||||
@ -173,6 +200,13 @@ static void member_to_text(struct printbuf *out,
|
||||
prt_units_u64(out, device_size << 9);
|
||||
prt_newline(out);
|
||||
|
||||
for (unsigned i = 0; i < BCH_MEMBER_ERROR_NR; i++) {
|
||||
prt_printf(out, "%s errors:", bch2_member_error_strs[i]);
|
||||
prt_tab(out);
|
||||
prt_u64(out, le64_to_cpu(m.errors[i]));
|
||||
prt_newline(out);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < BCH_IOPS_NR; i++) {
|
||||
prt_printf(out, "%s iops:", bch2_iops_measurements[i]);
|
||||
prt_tab(out);
|
||||
@ -211,21 +245,6 @@ static void member_to_text(struct printbuf *out,
|
||||
: "unknown");
|
||||
prt_newline(out);
|
||||
|
||||
prt_printf(out, "Label:");
|
||||
prt_tab(out);
|
||||
if (BCH_MEMBER_GROUP(&m)) {
|
||||
unsigned idx = BCH_MEMBER_GROUP(&m) - 1;
|
||||
|
||||
if (idx < disk_groups_nr(gi))
|
||||
prt_printf(out, "%s (%u)",
|
||||
gi->entries[idx].label, idx);
|
||||
else
|
||||
prt_printf(out, "(bad disk labels section)");
|
||||
} else {
|
||||
prt_printf(out, "(none)");
|
||||
}
|
||||
prt_newline(out);
|
||||
|
||||
prt_printf(out, "Data allowed:");
|
||||
prt_tab(out);
|
||||
if (BCH_MEMBER_DATA_ALLOWED(&m))
|
||||
@ -262,8 +281,7 @@ static int bch2_sb_members_v1_validate(struct bch_sb *sb,
|
||||
struct bch_sb_field_members_v1 *mi = field_to_type(f, members_v1);
|
||||
unsigned i;
|
||||
|
||||
if ((void *) members_v1_get_mut(mi, sb->nr_devices) >
|
||||
vstruct_end(&mi->field)) {
|
||||
if ((void *) members_v1_get_mut(mi, sb->nr_devices) > vstruct_end(&mi->field)) {
|
||||
prt_printf(err, "too many devices for section size");
|
||||
return -BCH_ERR_invalid_sb_members;
|
||||
}
|
||||
@ -337,3 +355,72 @@ const struct bch_sb_field_ops bch_sb_field_ops_members_v2 = {
|
||||
.validate = bch2_sb_members_v2_validate,
|
||||
.to_text = bch2_sb_members_v2_to_text,
|
||||
};
|
||||
|
||||
void bch2_sb_members_from_cpu(struct bch_fs *c)
|
||||
{
|
||||
struct bch_sb_field_members_v2 *mi = bch2_sb_field_get(c->disk_sb.sb, members_v2);
|
||||
struct bch_dev *ca;
|
||||
unsigned i, e;
|
||||
|
||||
rcu_read_lock();
|
||||
for_each_member_device_rcu(ca, c, i, NULL) {
|
||||
struct bch_member *m = members_v2_get_mut(mi, i);
|
||||
|
||||
for (e = 0; e < BCH_MEMBER_ERROR_NR; e++)
|
||||
m->errors[e] = cpu_to_le64(atomic64_read(&ca->errors[e]));
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void bch2_dev_io_errors_to_text(struct printbuf *out, struct bch_dev *ca)
|
||||
{
|
||||
struct bch_fs *c = ca->fs;
|
||||
struct bch_member m;
|
||||
|
||||
mutex_lock(&ca->fs->sb_lock);
|
||||
m = bch2_sb_member_get(c->disk_sb.sb, ca->dev_idx);
|
||||
mutex_unlock(&ca->fs->sb_lock);
|
||||
|
||||
printbuf_tabstop_push(out, 12);
|
||||
|
||||
prt_str(out, "IO errors since filesystem creation");
|
||||
prt_newline(out);
|
||||
|
||||
printbuf_indent_add(out, 2);
|
||||
for (unsigned i = 0; i < BCH_MEMBER_ERROR_NR; i++) {
|
||||
prt_printf(out, "%s:", bch2_member_error_strs[i]);
|
||||
prt_tab(out);
|
||||
prt_u64(out, atomic64_read(&ca->errors[i]));
|
||||
prt_newline(out);
|
||||
}
|
||||
printbuf_indent_sub(out, 2);
|
||||
|
||||
prt_str(out, "IO errors since ");
|
||||
bch2_pr_time_units(out, (ktime_get_real_seconds() - le64_to_cpu(m.errors_reset_time)) * NSEC_PER_SEC);
|
||||
prt_str(out, " ago");
|
||||
prt_newline(out);
|
||||
|
||||
printbuf_indent_add(out, 2);
|
||||
for (unsigned i = 0; i < BCH_MEMBER_ERROR_NR; i++) {
|
||||
prt_printf(out, "%s:", bch2_member_error_strs[i]);
|
||||
prt_tab(out);
|
||||
prt_u64(out, atomic64_read(&ca->errors[i]) - le64_to_cpu(m.errors_at_reset[i]));
|
||||
prt_newline(out);
|
||||
}
|
||||
printbuf_indent_sub(out, 2);
|
||||
}
|
||||
|
||||
void bch2_dev_errors_reset(struct bch_dev *ca)
|
||||
{
|
||||
struct bch_fs *c = ca->fs;
|
||||
struct bch_member *m;
|
||||
|
||||
mutex_lock(&c->sb_lock);
|
||||
m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(m->errors_at_reset); i++)
|
||||
m->errors_at_reset[i] = cpu_to_le64(atomic64_read(&ca->errors[i]));
|
||||
m->errors_reset_time = ktime_get_real_seconds();
|
||||
|
||||
bch2_write_super(c);
|
||||
mutex_unlock(&c->sb_lock);
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
#ifndef _BCACHEFS_SB_MEMBERS_H
|
||||
#define _BCACHEFS_SB_MEMBERS_H
|
||||
|
||||
int bch2_members_v2_init(struct bch_fs *c);
|
||||
int bch_members_cpy_v2_v1(struct bch_sb_handle *disk_sb);
|
||||
extern char * const bch2_member_error_strs[];
|
||||
|
||||
int bch2_sb_members_v2_init(struct bch_fs *c);
|
||||
int bch2_sb_members_cpy_v2_v1(struct bch_sb_handle *disk_sb);
|
||||
struct bch_member *bch2_members_v2_get_mut(struct bch_sb *sb, int i);
|
||||
struct bch_member bch2_sb_member_get(struct bch_sb *sb, int i);
|
||||
|
||||
@ -179,4 +181,42 @@ static inline struct bch_devs_mask bch2_online_devs(struct bch_fs *c)
|
||||
extern const struct bch_sb_field_ops bch_sb_field_ops_members_v1;
|
||||
extern const struct bch_sb_field_ops bch_sb_field_ops_members_v2;
|
||||
|
||||
static inline bool bch2_member_exists(struct bch_member *m)
|
||||
{
|
||||
return !bch2_is_zero(&m->uuid, sizeof(m->uuid));
|
||||
}
|
||||
|
||||
static inline bool bch2_dev_exists(struct bch_sb *sb,
|
||||
unsigned dev)
|
||||
{
|
||||
if (dev < sb->nr_devices) {
|
||||
struct bch_member m = bch2_sb_member_get(sb, dev);
|
||||
return bch2_member_exists(&m);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi)
|
||||
{
|
||||
return (struct bch_member_cpu) {
|
||||
.nbuckets = le64_to_cpu(mi->nbuckets),
|
||||
.first_bucket = le16_to_cpu(mi->first_bucket),
|
||||
.bucket_size = le16_to_cpu(mi->bucket_size),
|
||||
.group = BCH_MEMBER_GROUP(mi),
|
||||
.state = BCH_MEMBER_STATE(mi),
|
||||
.discard = BCH_MEMBER_DISCARD(mi),
|
||||
.data_allowed = BCH_MEMBER_DATA_ALLOWED(mi),
|
||||
.durability = BCH_MEMBER_DURABILITY(mi)
|
||||
? BCH_MEMBER_DURABILITY(mi) - 1
|
||||
: 1,
|
||||
.freespace_initialized = BCH_MEMBER_FREESPACE_INITIALIZED(mi),
|
||||
.valid = bch2_member_exists(mi),
|
||||
};
|
||||
}
|
||||
|
||||
void bch2_sb_members_from_cpu(struct bch_fs *);
|
||||
|
||||
void bch2_dev_io_errors_to_text(struct printbuf *, struct bch_dev *);
|
||||
void bch2_dev_errors_reset(struct bch_dev *);
|
||||
|
||||
#endif /* _BCACHEFS_SB_MEMBERS_H */
|
||||
|
@ -30,17 +30,18 @@ void bch2_snapshot_tree_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
le32_to_cpu(t.v->root_snapshot));
|
||||
}
|
||||
|
||||
int bch2_snapshot_tree_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_snapshot_tree_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
if (bkey_gt(k.k->p, POS(0, U32_MAX)) ||
|
||||
bkey_lt(k.k->p, POS(0, 1))) {
|
||||
prt_printf(err, "bad pos");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(bkey_gt(k.k->p, POS(0, U32_MAX)) ||
|
||||
bkey_lt(k.k->p, POS(0, 1)), c, err,
|
||||
snapshot_tree_pos_bad,
|
||||
"bad pos");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_snapshot_tree_lookup(struct btree_trans *trans, u32 id,
|
||||
@ -202,68 +203,60 @@ void bch2_snapshot_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
le32_to_cpu(s.v->skip[2]));
|
||||
}
|
||||
|
||||
int bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_snapshot_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
struct bkey_s_c_snapshot s;
|
||||
u32 i, id;
|
||||
int ret = 0;
|
||||
|
||||
if (bkey_gt(k.k->p, POS(0, U32_MAX)) ||
|
||||
bkey_lt(k.k->p, POS(0, 1))) {
|
||||
prt_printf(err, "bad pos");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bkey_gt(k.k->p, POS(0, U32_MAX)) ||
|
||||
bkey_lt(k.k->p, POS(0, 1)), c, err,
|
||||
snapshot_pos_bad,
|
||||
"bad pos");
|
||||
|
||||
s = bkey_s_c_to_snapshot(k);
|
||||
|
||||
id = le32_to_cpu(s.v->parent);
|
||||
if (id && id <= k.k->p.offset) {
|
||||
prt_printf(err, "bad parent node (%u <= %llu)",
|
||||
id, k.k->p.offset);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(id && id <= k.k->p.offset, c, err,
|
||||
snapshot_parent_bad,
|
||||
"bad parent node (%u <= %llu)",
|
||||
id, k.k->p.offset);
|
||||
|
||||
if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1])) {
|
||||
prt_printf(err, "children not normalized");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1]), c, err,
|
||||
snapshot_children_not_normalized,
|
||||
"children not normalized");
|
||||
|
||||
if (s.v->children[0] &&
|
||||
s.v->children[0] == s.v->children[1]) {
|
||||
prt_printf(err, "duplicate child nodes");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(s.v->children[0] && s.v->children[0] == s.v->children[1], c, err,
|
||||
snapshot_child_duplicate,
|
||||
"duplicate child nodes");
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
id = le32_to_cpu(s.v->children[i]);
|
||||
|
||||
if (id >= k.k->p.offset) {
|
||||
prt_printf(err, "bad child node (%u >= %llu)",
|
||||
id, k.k->p.offset);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(id >= k.k->p.offset, c, err,
|
||||
snapshot_child_bad,
|
||||
"bad child node (%u >= %llu)",
|
||||
id, k.k->p.offset);
|
||||
}
|
||||
|
||||
if (bkey_val_bytes(k.k) > offsetof(struct bch_snapshot, skip)) {
|
||||
if (le32_to_cpu(s.v->skip[0]) > le32_to_cpu(s.v->skip[1]) ||
|
||||
le32_to_cpu(s.v->skip[1]) > le32_to_cpu(s.v->skip[2])) {
|
||||
prt_printf(err, "skiplist not normalized");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(le32_to_cpu(s.v->skip[0]) > le32_to_cpu(s.v->skip[1]) ||
|
||||
le32_to_cpu(s.v->skip[1]) > le32_to_cpu(s.v->skip[2]), c, err,
|
||||
snapshot_skiplist_not_normalized,
|
||||
"skiplist not normalized");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s.v->skip); i++) {
|
||||
id = le32_to_cpu(s.v->skip[i]);
|
||||
|
||||
if ((id && !s.v->parent) ||
|
||||
(id && id <= k.k->p.offset)) {
|
||||
prt_printf(err, "bad skiplist node %u", id);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(id && id < le32_to_cpu(s.v->parent), c, err,
|
||||
snapshot_skiplist_bad,
|
||||
"bad skiplist node %u", id);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __set_is_ancestor_bitmap(struct bch_fs *c, u32 id)
|
||||
@ -530,7 +523,7 @@ static int check_snapshot_tree(struct btree_trans *trans,
|
||||
if (fsck_err_on(ret ||
|
||||
root_id != bch2_snapshot_root(c, root_id) ||
|
||||
st.k->p.offset != le32_to_cpu(s.tree),
|
||||
c,
|
||||
c, snapshot_tree_to_missing_snapshot,
|
||||
"snapshot tree points to missing/incorrect snapshot:\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf))) {
|
||||
ret = bch2_btree_delete_at(trans, iter, 0);
|
||||
@ -542,17 +535,20 @@ static int check_snapshot_tree(struct btree_trans *trans,
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(ret, c,
|
||||
if (fsck_err_on(ret,
|
||||
c, snapshot_tree_to_missing_subvol,
|
||||
"snapshot tree points to missing subvolume:\n %s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) ||
|
||||
fsck_err_on(!bch2_snapshot_is_ancestor_early(c,
|
||||
le32_to_cpu(subvol.snapshot),
|
||||
root_id), c,
|
||||
root_id),
|
||||
c, snapshot_tree_to_wrong_subvol,
|
||||
"snapshot tree points to subvolume that does not point to snapshot in this tree:\n %s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) ||
|
||||
fsck_err_on(BCH_SUBVOLUME_SNAP(&subvol), c,
|
||||
fsck_err_on(BCH_SUBVOLUME_SNAP(&subvol),
|
||||
c, snapshot_tree_to_snapshot_subvol,
|
||||
"snapshot tree points to snapshot subvolume:\n %s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf))) {
|
||||
@ -788,7 +784,9 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
if (fsck_err_on(s.subvol, c, "snapshot should not point to subvol:\n %s",
|
||||
if (fsck_err_on(s.subvol,
|
||||
c, snapshot_should_not_have_subvol,
|
||||
"snapshot should not point to subvol:\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
|
||||
ret = PTR_ERR_OR_ZERO(u);
|
||||
@ -804,7 +802,8 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(!ret, c, "snapshot points to missing/incorrect tree:\n %s",
|
||||
if (fsck_err_on(!ret, c, snapshot_to_bad_snapshot_tree,
|
||||
"snapshot points to missing/incorrect tree:\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
ret = snapshot_tree_ptr_repair(trans, iter, k, &s);
|
||||
if (ret)
|
||||
@ -816,7 +815,8 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
|
||||
if (le32_to_cpu(s.depth) != real_depth &&
|
||||
(c->sb.version_upgrade_complete < bcachefs_metadata_version_snapshot_skiplists ||
|
||||
fsck_err(c, "snapshot with incorrect depth field, should be %u:\n %s",
|
||||
fsck_err(c, snapshot_bad_depth,
|
||||
"snapshot with incorrect depth field, should be %u:\n %s",
|
||||
real_depth, (bch2_bkey_val_to_text(&buf, c, k), buf.buf)))) {
|
||||
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
|
||||
ret = PTR_ERR_OR_ZERO(u);
|
||||
@ -833,7 +833,8 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
|
||||
if (!ret &&
|
||||
(c->sb.version_upgrade_complete < bcachefs_metadata_version_snapshot_skiplists ||
|
||||
fsck_err(c, "snapshot with bad skiplist field:\n %s",
|
||||
fsck_err(c, snapshot_bad_skiplist,
|
||||
"snapshot with bad skiplist field:\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))) {
|
||||
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
|
||||
ret = PTR_ERR_OR_ZERO(u);
|
||||
@ -1348,12 +1349,12 @@ static int bch2_fix_child_of_deleted_snapshot(struct btree_trans *trans,
|
||||
u32 id = le32_to_cpu(s->v.skip[j]);
|
||||
|
||||
if (snapshot_list_has_id(deleted, id)) {
|
||||
id = depth > 1
|
||||
? bch2_snapshot_nth_parent_skip(c,
|
||||
id = bch2_snapshot_nth_parent_skip(c,
|
||||
parent,
|
||||
get_random_u32_below(depth - 1),
|
||||
deleted)
|
||||
: parent;
|
||||
depth > 1
|
||||
? get_random_u32_below(depth - 1)
|
||||
: 0,
|
||||
deleted);
|
||||
s->v.skip[j] = cpu_to_le32(id);
|
||||
}
|
||||
}
|
||||
@ -1436,6 +1437,15 @@ int bch2_delete_dead_snapshots(struct bch_fs *c)
|
||||
if (!btree_type_has_snapshots(id))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* deleted inodes btree is maintained by a trigger on the inodes
|
||||
* btree - no work for us to do here, and it's not safe to scan
|
||||
* it because we'll see out of date keys due to the btree write
|
||||
* buffer:
|
||||
*/
|
||||
if (id == BTREE_ID_deleted_inodes)
|
||||
continue;
|
||||
|
||||
ret = for_each_btree_key_commit(trans, iter,
|
||||
id, POS_MIN,
|
||||
BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
|
||||
|
@ -5,7 +5,7 @@
|
||||
enum bkey_invalid_flags;
|
||||
|
||||
void bch2_snapshot_tree_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
int bch2_snapshot_tree_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_snapshot_tree_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
|
||||
#define bch2_bkey_ops_snapshot_tree ((struct bkey_ops) { \
|
||||
@ -19,7 +19,7 @@ struct bkey_i_snapshot_tree *__bch2_snapshot_tree_create(struct btree_trans *);
|
||||
int bch2_snapshot_tree_lookup(struct btree_trans *, u32, struct bch_snapshot_tree *);
|
||||
|
||||
void bch2_snapshot_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
int bch2_snapshot_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_snapshot_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
int bch2_mark_snapshot(struct btree_trans *, enum btree_id, unsigned,
|
||||
struct bkey_s_c, struct bkey_s_c, unsigned);
|
||||
|
@ -62,7 +62,8 @@ static int check_subvol(struct btree_trans *trans,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (fsck_err_on(le32_to_cpu(st.master_subvol) != subvol.k->p.offset, c,
|
||||
if (fsck_err_on(le32_to_cpu(st.master_subvol) != subvol.k->p.offset,
|
||||
c, subvol_not_master_and_not_snapshot,
|
||||
"subvolume %llu is not set as snapshot but is not master subvolume",
|
||||
k.k->p.offset)) {
|
||||
struct bkey_i_subvolume *s =
|
||||
@ -97,16 +98,17 @@ int bch2_check_subvols(struct bch_fs *c)
|
||||
|
||||
/* Subvolumes: */
|
||||
|
||||
int bch2_subvolume_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_subvolume_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags, struct printbuf *err)
|
||||
{
|
||||
if (bkey_lt(k.k->p, SUBVOL_POS_MIN) ||
|
||||
bkey_gt(k.k->p, SUBVOL_POS_MAX)) {
|
||||
prt_printf(err, "invalid pos");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(bkey_lt(k.k->p, SUBVOL_POS_MIN) ||
|
||||
bkey_gt(k.k->p, SUBVOL_POS_MAX), c, err,
|
||||
subvol_pos_bad,
|
||||
"invalid pos");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
|
@ -9,7 +9,7 @@ enum bkey_invalid_flags;
|
||||
|
||||
int bch2_check_subvols(struct bch_fs *);
|
||||
|
||||
int bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_subvolume_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "replicas.h"
|
||||
#include "quota.h"
|
||||
#include "sb-clean.h"
|
||||
#include "sb-errors.h"
|
||||
#include "sb-members.h"
|
||||
#include "super-io.h"
|
||||
#include "super.h"
|
||||
@ -805,7 +806,12 @@ static void write_super_endio(struct bio *bio)
|
||||
|
||||
/* XXX: return errors directly */
|
||||
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca, "superblock write error: %s",
|
||||
if (bch2_dev_io_err_on(bio->bi_status, ca,
|
||||
bio_data_dir(bio)
|
||||
? BCH_MEMBER_ERROR_write
|
||||
: BCH_MEMBER_ERROR_read,
|
||||
"superblock %s error: %s",
|
||||
bio_data_dir(bio) ? "write" : "read",
|
||||
bch2_blk_status_to_str(bio->bi_status)))
|
||||
ca->sb_write_error = 1;
|
||||
|
||||
@ -892,7 +898,9 @@ int bch2_write_super(struct bch_fs *c)
|
||||
SET_BCH_SB_BIG_ENDIAN(c->disk_sb.sb, CPU_BIG_ENDIAN);
|
||||
|
||||
bch2_sb_counters_from_cpu(c);
|
||||
bch_members_cpy_v2_v1(&c->disk_sb);
|
||||
bch2_sb_members_from_cpu(c);
|
||||
bch2_sb_members_cpy_v2_v1(&c->disk_sb);
|
||||
bch2_sb_errors_from_cpu(c);
|
||||
|
||||
for_each_online_member(ca, c, i)
|
||||
bch2_sb_from_fs(c, ca);
|
||||
|
@ -23,6 +23,11 @@ u64 bch2_upgrade_recovery_passes(struct bch_fs *c,
|
||||
unsigned,
|
||||
unsigned);
|
||||
|
||||
static inline size_t bch2_sb_field_bytes(struct bch_sb_field *f)
|
||||
{
|
||||
return le32_to_cpu(f->u64s) * sizeof(u64);
|
||||
}
|
||||
|
||||
#define field_to_type(_f, _name) \
|
||||
container_of_or_null(_f, struct bch_sb_field_##_name, field)
|
||||
|
||||
@ -78,41 +83,6 @@ static inline void bch2_check_set_feature(struct bch_fs *c, unsigned feat)
|
||||
__bch2_check_set_feature(c, feat);
|
||||
}
|
||||
|
||||
/* BCH_SB_FIELD_members_v1: */
|
||||
|
||||
static inline bool bch2_member_exists(struct bch_member *m)
|
||||
{
|
||||
return !bch2_is_zero(&m->uuid, sizeof(m->uuid));
|
||||
}
|
||||
|
||||
static inline bool bch2_dev_exists(struct bch_sb *sb,
|
||||
unsigned dev)
|
||||
{
|
||||
if (dev < sb->nr_devices) {
|
||||
struct bch_member m = bch2_sb_member_get(sb, dev);
|
||||
return bch2_member_exists(&m);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi)
|
||||
{
|
||||
return (struct bch_member_cpu) {
|
||||
.nbuckets = le64_to_cpu(mi->nbuckets),
|
||||
.first_bucket = le16_to_cpu(mi->first_bucket),
|
||||
.bucket_size = le16_to_cpu(mi->bucket_size),
|
||||
.group = BCH_MEMBER_GROUP(mi),
|
||||
.state = BCH_MEMBER_STATE(mi),
|
||||
.discard = BCH_MEMBER_DISCARD(mi),
|
||||
.data_allowed = BCH_MEMBER_DATA_ALLOWED(mi),
|
||||
.durability = BCH_MEMBER_DURABILITY(mi)
|
||||
? BCH_MEMBER_DURABILITY(mi) - 1
|
||||
: 1,
|
||||
.freespace_initialized = BCH_MEMBER_FREESPACE_INITIALIZED(mi),
|
||||
.valid = bch2_member_exists(mi),
|
||||
};
|
||||
}
|
||||
|
||||
void bch2_sb_maybe_downgrade(struct bch_fs *);
|
||||
void bch2_sb_upgrade(struct bch_fs *, unsigned);
|
||||
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "recovery.h"
|
||||
#include "replicas.h"
|
||||
#include "sb-clean.h"
|
||||
#include "sb-errors.h"
|
||||
#include "sb-members.h"
|
||||
#include "snapshot.h"
|
||||
#include "subvolume.h"
|
||||
@ -400,7 +401,7 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
|
||||
|
||||
bch_info(c, "going read-write");
|
||||
|
||||
ret = bch2_members_v2_init(c);
|
||||
ret = bch2_sb_members_v2_init(c);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -481,6 +482,7 @@ static void __bch2_fs_free(struct bch_fs *c)
|
||||
bch2_time_stats_exit(&c->times[i]);
|
||||
|
||||
bch2_free_pending_node_rewrites(c);
|
||||
bch2_fs_sb_errors_exit(c);
|
||||
bch2_fs_counters_exit(c);
|
||||
bch2_fs_snapshots_exit(c);
|
||||
bch2_fs_quota_exit(c);
|
||||
@ -713,6 +715,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
|
||||
bch2_fs_quota_init(c);
|
||||
bch2_fs_ec_init_early(c);
|
||||
bch2_fs_move_init(c);
|
||||
bch2_fs_sb_errors_init_early(c);
|
||||
|
||||
INIT_LIST_HEAD(&c->list);
|
||||
|
||||
@ -729,8 +732,8 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
|
||||
|
||||
INIT_LIST_HEAD(&c->journal_iters);
|
||||
|
||||
INIT_LIST_HEAD(&c->fsck_errors);
|
||||
mutex_init(&c->fsck_error_lock);
|
||||
INIT_LIST_HEAD(&c->fsck_error_msgs);
|
||||
mutex_init(&c->fsck_error_msgs_lock);
|
||||
|
||||
seqcount_init(&c->gc_pos_lock);
|
||||
|
||||
@ -840,6 +843,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
|
||||
}
|
||||
|
||||
ret = bch2_fs_counters_init(c) ?:
|
||||
bch2_fs_sb_errors_init(c) ?:
|
||||
bch2_io_clock_init(&c->io_clock[READ]) ?:
|
||||
bch2_io_clock_init(&c->io_clock[WRITE]) ?:
|
||||
bch2_fs_journal_init(&c->journal) ?:
|
||||
@ -942,7 +946,7 @@ int bch2_fs_start(struct bch_fs *c)
|
||||
|
||||
mutex_lock(&c->sb_lock);
|
||||
|
||||
ret = bch2_members_v2_init(c);
|
||||
ret = bch2_sb_members_v2_init(c);
|
||||
if (ret) {
|
||||
mutex_unlock(&c->sb_lock);
|
||||
goto err;
|
||||
@ -1131,6 +1135,7 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
|
||||
struct bch_member *member)
|
||||
{
|
||||
struct bch_dev *ca;
|
||||
unsigned i;
|
||||
|
||||
ca = kzalloc(sizeof(*ca), GFP_KERNEL);
|
||||
if (!ca)
|
||||
@ -1148,6 +1153,10 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
|
||||
bch2_time_stats_init(&ca->io_latency[WRITE]);
|
||||
|
||||
ca->mi = bch2_mi_to_cpu(member);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(member->errors); i++)
|
||||
atomic64_set(&ca->errors[i], le64_to_cpu(member->errors[i]));
|
||||
|
||||
ca->uuid = member->uuid;
|
||||
|
||||
ca->nr_btree_reserve = DIV_ROUND_UP(BTREE_NODE_RESERVE,
|
||||
@ -1622,16 +1631,6 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
mi = bch2_sb_field_get(ca->disk_sb.sb, members_v2);
|
||||
|
||||
if (!bch2_sb_field_resize(&ca->disk_sb, members_v2,
|
||||
le32_to_cpu(mi->field.u64s) +
|
||||
sizeof(dev_mi) / sizeof(u64))) {
|
||||
ret = -BCH_ERR_ENOSPC_sb_members;
|
||||
bch_err_msg(c, ret, "setting up new superblock");
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
if (dynamic_fault("bcachefs:add:no_slot"))
|
||||
goto no_slot;
|
||||
|
||||
@ -1645,6 +1644,8 @@ no_slot:
|
||||
|
||||
have_slot:
|
||||
nr_devices = max_t(unsigned, dev_idx + 1, c->sb.nr_devices);
|
||||
|
||||
mi = bch2_sb_field_get(c->disk_sb.sb, members_v2);
|
||||
u64s = DIV_ROUND_UP(sizeof(struct bch_sb_field_members_v2) +
|
||||
le16_to_cpu(mi->member_bytes) * nr_devices, sizeof(u64));
|
||||
|
||||
|
@ -149,7 +149,9 @@ read_attribute(bucket_size);
|
||||
read_attribute(first_bucket);
|
||||
read_attribute(nbuckets);
|
||||
rw_attribute(durability);
|
||||
read_attribute(iodone);
|
||||
read_attribute(io_done);
|
||||
read_attribute(io_errors);
|
||||
write_attribute(io_errors_reset);
|
||||
|
||||
read_attribute(io_latency_read);
|
||||
read_attribute(io_latency_write);
|
||||
@ -880,7 +882,7 @@ static const char * const bch2_rw[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static void dev_iodone_to_text(struct printbuf *out, struct bch_dev *ca)
|
||||
static void dev_io_done_to_text(struct printbuf *out, struct bch_dev *ca)
|
||||
{
|
||||
int rw, i;
|
||||
|
||||
@ -923,8 +925,11 @@ SHOW(bch2_dev)
|
||||
prt_char(out, '\n');
|
||||
}
|
||||
|
||||
if (attr == &sysfs_iodone)
|
||||
dev_iodone_to_text(out, ca);
|
||||
if (attr == &sysfs_io_done)
|
||||
dev_io_done_to_text(out, ca);
|
||||
|
||||
if (attr == &sysfs_io_errors)
|
||||
bch2_dev_io_errors_to_text(out, ca);
|
||||
|
||||
sysfs_print(io_latency_read, atomic64_read(&ca->cur_latency[READ]));
|
||||
sysfs_print(io_latency_write, atomic64_read(&ca->cur_latency[WRITE]));
|
||||
@ -991,6 +996,9 @@ STORE(bch2_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (attr == &sysfs_io_errors_reset)
|
||||
bch2_dev_errors_reset(ca);
|
||||
|
||||
return size;
|
||||
}
|
||||
SYSFS_OPS(bch2_dev);
|
||||
@ -1008,7 +1016,9 @@ struct attribute *bch2_dev_files[] = {
|
||||
&sysfs_label,
|
||||
|
||||
&sysfs_has_data,
|
||||
&sysfs_iodone,
|
||||
&sysfs_io_done,
|
||||
&sysfs_io_errors,
|
||||
&sysfs_io_errors_reset,
|
||||
|
||||
&sysfs_io_latency_read,
|
||||
&sysfs_io_latency_write,
|
||||
|
@ -70,46 +70,38 @@ const struct bch_hash_desc bch2_xattr_hash_desc = {
|
||||
.cmp_bkey = xattr_cmp_bkey,
|
||||
};
|
||||
|
||||
int bch2_xattr_invalid(const struct bch_fs *c, struct bkey_s_c k,
|
||||
int bch2_xattr_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
enum bkey_invalid_flags flags,
|
||||
struct printbuf *err)
|
||||
{
|
||||
const struct xattr_handler *handler;
|
||||
struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k);
|
||||
unsigned val_u64s = xattr_val_u64s(xattr.v->x_name_len,
|
||||
le16_to_cpu(xattr.v->x_val_len));
|
||||
int ret = 0;
|
||||
|
||||
if (bkey_val_u64s(k.k) <
|
||||
xattr_val_u64s(xattr.v->x_name_len,
|
||||
le16_to_cpu(xattr.v->x_val_len))) {
|
||||
prt_printf(err, "value too small (%zu < %u)",
|
||||
bkey_val_u64s(k.k),
|
||||
xattr_val_u64s(xattr.v->x_name_len,
|
||||
le16_to_cpu(xattr.v->x_val_len)));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bkey_val_u64s(k.k) < val_u64s, c, err,
|
||||
xattr_val_size_too_small,
|
||||
"value too small (%zu < %u)",
|
||||
bkey_val_u64s(k.k), val_u64s);
|
||||
|
||||
/* XXX why +4 ? */
|
||||
if (bkey_val_u64s(k.k) >
|
||||
xattr_val_u64s(xattr.v->x_name_len,
|
||||
le16_to_cpu(xattr.v->x_val_len) + 4)) {
|
||||
prt_printf(err, "value too big (%zu > %u)",
|
||||
bkey_val_u64s(k.k),
|
||||
xattr_val_u64s(xattr.v->x_name_len,
|
||||
le16_to_cpu(xattr.v->x_val_len) + 4));
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
val_u64s = xattr_val_u64s(xattr.v->x_name_len,
|
||||
le16_to_cpu(xattr.v->x_val_len) + 4);
|
||||
|
||||
handler = bch2_xattr_type_to_handler(xattr.v->x_type);
|
||||
if (!handler) {
|
||||
prt_printf(err, "invalid type (%u)", xattr.v->x_type);
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(bkey_val_u64s(k.k) > val_u64s, c, err,
|
||||
xattr_val_size_too_big,
|
||||
"value too big (%zu > %u)",
|
||||
bkey_val_u64s(k.k), val_u64s);
|
||||
|
||||
if (memchr(xattr.v->x_name, '\0', xattr.v->x_name_len)) {
|
||||
prt_printf(err, "xattr name has invalid characters");
|
||||
return -BCH_ERR_invalid_bkey;
|
||||
}
|
||||
bkey_fsck_err_on(!bch2_xattr_type_to_handler(xattr.v->x_type), c, err,
|
||||
xattr_invalid_type,
|
||||
"invalid type (%u)", xattr.v->x_type);
|
||||
|
||||
return 0;
|
||||
bkey_fsck_err_on(memchr(xattr.v->x_name, '\0', xattr.v->x_name_len), c, err,
|
||||
xattr_name_invalid_chars,
|
||||
"xattr name has invalid characters");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_xattr_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
extern const struct bch_hash_desc bch2_xattr_hash_desc;
|
||||
|
||||
int bch2_xattr_invalid(const struct bch_fs *, struct bkey_s_c,
|
||||
int bch2_xattr_invalid(struct bch_fs *, struct bkey_s_c,
|
||||
enum bkey_invalid_flags, struct printbuf *);
|
||||
void bch2_xattr_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user