Commit f0ac7df2 authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: Convert .key_invalid methods to printbufs

Signed-off-by: default avatarKent Overstreet <kent.overstreet@gmail.com>
parent d1d7737f
...@@ -302,71 +302,86 @@ static unsigned bch_alloc_v1_val_u64s(const struct bch_alloc *a) ...@@ -302,71 +302,86 @@ static unsigned bch_alloc_v1_val_u64s(const struct bch_alloc *a)
return DIV_ROUND_UP(bytes, sizeof(u64)); return DIV_ROUND_UP(bytes, sizeof(u64));
} }
const char *bch2_alloc_v1_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_alloc_v1_invalid(const struct bch_fs *c, struct bkey_s_c k, struct printbuf *err)
{ {
struct bkey_s_c_alloc a = bkey_s_c_to_alloc(k); struct bkey_s_c_alloc a = bkey_s_c_to_alloc(k);
if (k.k->p.inode >= c->sb.nr_devices || if (!bch2_dev_exists2(c, k.k->p.inode)) {
!c->devs[k.k->p.inode]) pr_buf(err, "invalid device (%llu)", k.k->p.inode);
return "invalid device"; return -EINVAL;
}
/* allow for unknown fields */ /* allow for unknown fields */
if (bkey_val_u64s(a.k) < bch_alloc_v1_val_u64s(a.v)) if (bkey_val_u64s(a.k) < bch_alloc_v1_val_u64s(a.v)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu < %u)",
bkey_val_u64s(a.k), bch_alloc_v1_val_u64s(a.v));
return -EINVAL;
}
return NULL; return 0;
} }
const char *bch2_alloc_v2_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_alloc_v2_invalid(const struct bch_fs *c, struct bkey_s_c k, struct printbuf *err)
{ {
struct bkey_alloc_unpacked u; struct bkey_alloc_unpacked u;
if (k.k->p.inode >= c->sb.nr_devices || if (!bch2_dev_exists2(c, k.k->p.inode)) {
!c->devs[k.k->p.inode]) pr_buf(err, "invalid device (%llu)", k.k->p.inode);
return "invalid device"; return -EINVAL;
}
if (bch2_alloc_unpack_v2(&u, k)) if (bch2_alloc_unpack_v2(&u, k)) {
return "unpack error"; pr_buf(err, "unpack error");
return -EINVAL;
}
return NULL; return 0;
} }
const char *bch2_alloc_v3_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_alloc_v3_invalid(const struct bch_fs *c, struct bkey_s_c k, struct printbuf *err)
{ {
struct bkey_alloc_unpacked u; struct bkey_alloc_unpacked u;
struct bch_dev *ca; struct bch_dev *ca;
if (k.k->p.inode >= c->sb.nr_devices || if (!bch2_dev_exists2(c, k.k->p.inode)) {
!c->devs[k.k->p.inode]) pr_buf(err, "invalid device (%llu)", k.k->p.inode);
return "invalid device"; return -EINVAL;
}
ca = bch_dev_bkey_exists(c, k.k->p.inode); ca = bch_dev_bkey_exists(c, k.k->p.inode);
if (k.k->p.offset < ca->mi.first_bucket || if (k.k->p.offset < ca->mi.first_bucket ||
k.k->p.offset >= ca->mi.nbuckets) k.k->p.offset >= ca->mi.nbuckets) {
return "invalid bucket"; pr_buf(err, "invalid bucket");
return -EINVAL;
}
if (bch2_alloc_unpack_v3(&u, k)) if (bch2_alloc_unpack_v3(&u, k)) {
return "unpack error"; pr_buf(err, "unpack error");
return -EINVAL;
}
return NULL; return 0;
} }
const char *bch2_alloc_v4_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_alloc_v4_invalid(const struct bch_fs *c, struct bkey_s_c k, struct printbuf *err)
{ {
struct bch_dev *ca; struct bch_dev *ca;
if (k.k->p.inode >= c->sb.nr_devices || if (!bch2_dev_exists2(c, k.k->p.inode)) {
!c->devs[k.k->p.inode]) pr_buf(err, "invalid device (%llu)", k.k->p.inode);
return "invalid device"; return -EINVAL;
}
ca = bch_dev_bkey_exists(c, k.k->p.inode); ca = bch_dev_bkey_exists(c, k.k->p.inode);
if (k.k->p.offset < ca->mi.first_bucket || if (k.k->p.offset < ca->mi.first_bucket ||
k.k->p.offset >= ca->mi.nbuckets) k.k->p.offset >= ca->mi.nbuckets) {
return "invalid bucket"; pr_buf(err, "invalid bucket");
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_alloc_v4_swab(struct bkey_s k) void bch2_alloc_v4_swab(struct bkey_s k)
......
...@@ -66,10 +66,10 @@ int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int); ...@@ -66,10 +66,10 @@ int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int);
#define ALLOC_SCAN_BATCH(ca) max_t(size_t, 1, (ca)->mi.nbuckets >> 9) #define ALLOC_SCAN_BATCH(ca) max_t(size_t, 1, (ca)->mi.nbuckets >> 9)
const char *bch2_alloc_v1_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_alloc_v1_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
const char *bch2_alloc_v2_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_alloc_v2_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
const char *bch2_alloc_v3_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_alloc_v3_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
const char *bch2_alloc_v4_invalid(const struct bch_fs *, struct bkey_s_c k); int bch2_alloc_v4_invalid(const struct bch_fs *, struct bkey_s_c k, struct printbuf *);
void bch2_alloc_v4_swab(struct bkey_s); void bch2_alloc_v4_swab(struct bkey_s);
void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
......
...@@ -22,10 +22,10 @@ const char * const bch2_bkey_types[] = { ...@@ -22,10 +22,10 @@ const char * const bch2_bkey_types[] = {
NULL NULL
}; };
static const char *deleted_key_invalid(const struct bch_fs *c, static int deleted_key_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct bkey_s_c k) struct printbuf *err)
{ {
return NULL; return 0;
} }
#define bch2_bkey_ops_deleted (struct bkey_ops) { \ #define bch2_bkey_ops_deleted (struct bkey_ops) { \
...@@ -36,25 +36,32 @@ static const char *deleted_key_invalid(const struct bch_fs *c, ...@@ -36,25 +36,32 @@ static const char *deleted_key_invalid(const struct bch_fs *c,
.key_invalid = deleted_key_invalid, \ .key_invalid = deleted_key_invalid, \
} }
static const char *empty_val_key_invalid(const struct bch_fs *c, struct bkey_s_c k) static int empty_val_key_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
if (bkey_val_bytes(k.k)) if (bkey_val_bytes(k.k)) {
return "value size should be zero"; pr_buf(err, "incorrect value size (%zu != 0)",
bkey_val_bytes(k.k));
return -EINVAL;
}
return NULL; return 0;
} }
#define bch2_bkey_ops_error (struct bkey_ops) { \ #define bch2_bkey_ops_error (struct bkey_ops) { \
.key_invalid = empty_val_key_invalid, \ .key_invalid = empty_val_key_invalid, \
} }
static const char *key_type_cookie_invalid(const struct bch_fs *c, static int key_type_cookie_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct bkey_s_c k) struct printbuf *err)
{ {
if (bkey_val_bytes(k.k) != sizeof(struct bch_cookie)) if (bkey_val_bytes(k.k) != sizeof(struct bch_cookie)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu != %zu)",
bkey_val_bytes(k.k), sizeof(struct bch_cookie));
return -EINVAL;
}
return NULL; return 0;
} }
#define bch2_bkey_ops_cookie (struct bkey_ops) { \ #define bch2_bkey_ops_cookie (struct bkey_ops) { \
...@@ -65,10 +72,10 @@ static const char *key_type_cookie_invalid(const struct bch_fs *c, ...@@ -65,10 +72,10 @@ static const char *key_type_cookie_invalid(const struct bch_fs *c,
.key_invalid = empty_val_key_invalid, \ .key_invalid = empty_val_key_invalid, \
} }
static const char *key_type_inline_data_invalid(const struct bch_fs *c, static int key_type_inline_data_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct bkey_s_c k) struct printbuf *err)
{ {
return NULL; return 0;
} }
static void key_type_inline_data_to_text(struct printbuf *out, struct bch_fs *c, static void key_type_inline_data_to_text(struct printbuf *out, struct bch_fs *c,
...@@ -86,11 +93,16 @@ static void key_type_inline_data_to_text(struct printbuf *out, struct bch_fs *c, ...@@ -86,11 +93,16 @@ static void key_type_inline_data_to_text(struct printbuf *out, struct bch_fs *c,
.val_to_text = key_type_inline_data_to_text, \ .val_to_text = key_type_inline_data_to_text, \
} }
static const char *key_type_set_invalid(const struct bch_fs *c, struct bkey_s_c k) static int key_type_set_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
if (bkey_val_bytes(k.k)) if (bkey_val_bytes(k.k)) {
return "nonempty value"; pr_buf(err, "incorrect value size (%zu != %zu)",
return NULL; bkey_val_bytes(k.k), sizeof(struct bch_cookie));
return -EINVAL;
}
return 0;
} }
static bool key_type_set_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r) static bool key_type_set_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r)
...@@ -110,12 +122,14 @@ const struct bkey_ops bch2_bkey_ops[] = { ...@@ -110,12 +122,14 @@ const struct bkey_ops bch2_bkey_ops[] = {
#undef x #undef x
}; };
const char *bch2_bkey_val_invalid(struct bch_fs *c, struct bkey_s_c k) int bch2_bkey_val_invalid(struct bch_fs *c, struct bkey_s_c k, struct printbuf *err)
{ {
if (k.k->type >= KEY_TYPE_MAX) if (k.k->type >= KEY_TYPE_MAX) {
return "invalid type"; pr_buf(err, "invalid type (%u >= %u)", k.k->type, KEY_TYPE_MAX);
return -EINVAL;
}
return bch2_bkey_ops[k.k->type].key_invalid(c, k); return bch2_bkey_ops[k.k->type].key_invalid(c, k, err);
} }
static unsigned bch2_key_types_allowed[] = { static unsigned bch2_key_types_allowed[] = {
...@@ -182,63 +196,84 @@ static unsigned bch2_key_types_allowed[] = { ...@@ -182,63 +196,84 @@ static unsigned bch2_key_types_allowed[] = {
(1U << KEY_TYPE_btree_ptr_v2), (1U << KEY_TYPE_btree_ptr_v2),
}; };
const char *__bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k, int __bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
enum btree_node_type type) enum btree_node_type type,
struct printbuf *err)
{ {
if (k.k->u64s < BKEY_U64s) if (k.k->u64s < BKEY_U64s) {
return "u64s too small"; pr_buf(err, "u64s too small (%u < %zu)", k.k->u64s, BKEY_U64s);
return -EINVAL;
if (!(bch2_key_types_allowed[type] & (1U << k.k->type))) }
return "invalid key type for this btree";
if (type == BKEY_TYPE_btree && if (!(bch2_key_types_allowed[type] & (1U << k.k->type))) {
bkey_val_u64s(k.k) > BKEY_BTREE_PTR_VAL_U64s_MAX) pr_buf(err, "invalid key type for this btree (%s)",
return "value too big"; bch2_bkey_types[type]);
return -EINVAL;
}
if (btree_node_type_is_extents(type) && !bkey_whiteout(k.k)) { if (btree_node_type_is_extents(type) && !bkey_whiteout(k.k)) {
if (k.k->size == 0) if (k.k->size == 0) {
return "bad size field"; pr_buf(err, "size == 0");
return -EINVAL;
}
if (k.k->size > k.k->p.offset) if (k.k->size > k.k->p.offset) {
return "size greater than offset"; pr_buf(err, "size greater than offset (%u > %llu)",
k.k->size, k.k->p.offset);
return -EINVAL;
}
} else { } else {
if (k.k->size) if (k.k->size) {
return "nonzero size field"; pr_buf(err, "size != 0");
return -EINVAL;
}
} }
if (type != BKEY_TYPE_btree && if (type != BKEY_TYPE_btree &&
!btree_type_has_snapshots(type) && !btree_type_has_snapshots(type) &&
k.k->p.snapshot) k.k->p.snapshot) {
return "nonzero snapshot"; pr_buf(err, "nonzero snapshot");
return -EINVAL;
}
if (type != BKEY_TYPE_btree && if (type != BKEY_TYPE_btree &&
btree_type_has_snapshots(type) && btree_type_has_snapshots(type) &&
!k.k->p.snapshot) !k.k->p.snapshot) {
return "invalid snapshot field"; pr_buf(err, "snapshot == 0");
return -EINVAL;
}
if (type != BKEY_TYPE_btree && if (type != BKEY_TYPE_btree &&
!bkey_cmp(k.k->p, POS_MAX)) !bkey_cmp(k.k->p, POS_MAX)) {
return "POS_MAX key"; pr_buf(err, "key at POS_MAX");
return -EINVAL;
}
return NULL; return 0;
} }
const char *bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k, int bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
enum btree_node_type type) enum btree_node_type type,
struct printbuf *err)
{ {
return __bch2_bkey_invalid(c, k, type) ?: return __bch2_bkey_invalid(c, k, type, err) ?:
bch2_bkey_val_invalid(c, k); bch2_bkey_val_invalid(c, k, err);
} }
const char *bch2_bkey_in_btree_node(struct btree *b, struct bkey_s_c k) int bch2_bkey_in_btree_node(struct btree *b, struct bkey_s_c k,
struct printbuf *err)
{ {
if (bpos_cmp(k.k->p, b->data->min_key) < 0) if (bpos_cmp(k.k->p, b->data->min_key) < 0) {
return "key before start of btree node"; pr_buf(err, "key before start of btree node");
return -EINVAL;
}
if (bpos_cmp(k.k->p, b->data->max_key) > 0) if (bpos_cmp(k.k->p, b->data->max_key) > 0) {
return "key past end of btree node"; pr_buf(err, "key past end of btree node");
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_bpos_to_text(struct printbuf *out, struct bpos pos) void bch2_bpos_to_text(struct printbuf *out, struct bpos pos)
......
...@@ -14,8 +14,8 @@ extern const char * const bch2_bkey_types[]; ...@@ -14,8 +14,8 @@ extern const char * const bch2_bkey_types[];
struct bkey_ops { struct bkey_ops {
/* Returns reason for being invalid if invalid, else NULL: */ /* Returns reason for being invalid if invalid, else NULL: */
const char * (*key_invalid)(const struct bch_fs *, int (*key_invalid)(const struct bch_fs *, struct bkey_s_c,
struct bkey_s_c); struct printbuf *);
void (*val_to_text)(struct printbuf *, struct bch_fs *, void (*val_to_text)(struct printbuf *, struct bch_fs *,
struct bkey_s_c); struct bkey_s_c);
void (*swab)(struct bkey_s); void (*swab)(struct bkey_s);
...@@ -32,12 +32,12 @@ struct bkey_ops { ...@@ -32,12 +32,12 @@ struct bkey_ops {
extern const struct bkey_ops bch2_bkey_ops[]; extern const struct bkey_ops bch2_bkey_ops[];
const char *bch2_bkey_val_invalid(struct bch_fs *, struct bkey_s_c); int bch2_bkey_val_invalid(struct bch_fs *, struct bkey_s_c, struct printbuf *);
const char *__bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c, int __bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c,
enum btree_node_type); enum btree_node_type, struct printbuf *);
const char *bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c, int bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c,
enum btree_node_type); enum btree_node_type, struct printbuf *);
const char *bch2_bkey_in_btree_node(struct btree *, struct bkey_s_c); int bch2_bkey_in_btree_node(struct btree *, struct bkey_s_c, struct printbuf *);
void bch2_bpos_to_text(struct printbuf *, struct bpos); void bch2_bpos_to_text(struct printbuf *, struct bpos);
void bch2_bkey_to_text(struct printbuf *, const struct bkey *); void bch2_bkey_to_text(struct printbuf *, const struct bkey *);
......
...@@ -762,14 +762,23 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca, ...@@ -762,14 +762,23 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
return ret; return ret;
} }
static int bset_key_invalid(struct bch_fs *c, struct btree *b,
struct bkey_s_c k,
bool updated_range, int write,
struct printbuf *err)
{
return __bch2_bkey_invalid(c, k, btree_node_type(b), err) ?:
(!updated_range ? bch2_bkey_in_btree_node(b, k, err) : 0) ?:
(write ? bch2_bkey_val_invalid(c, k, err) : 0);
}
static int validate_bset_keys(struct bch_fs *c, struct btree *b, static int validate_bset_keys(struct bch_fs *c, struct btree *b,
struct bset *i, unsigned *whiteout_u64s, struct bset *i, unsigned *whiteout_u64s,
int write, bool have_retry) int write, bool have_retry)
{ {
unsigned version = le16_to_cpu(i->version); unsigned version = le16_to_cpu(i->version);
struct bkey_packed *k, *prev = NULL; struct bkey_packed *k, *prev = NULL;
struct printbuf buf1 = PRINTBUF; struct printbuf buf = PRINTBUF;
struct printbuf buf2 = PRINTBUF;
bool updated_range = b->key.k.type == KEY_TYPE_btree_ptr_v2 && bool updated_range = b->key.k.type == KEY_TYPE_btree_ptr_v2 &&
BTREE_PTR_RANGE_UPDATED(&bkey_i_to_btree_ptr_v2(&b->key)->v); BTREE_PTR_RANGE_UPDATED(&bkey_i_to_btree_ptr_v2(&b->key)->v);
int ret = 0; int ret = 0;
...@@ -778,7 +787,6 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b, ...@@ -778,7 +787,6 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
k != vstruct_last(i);) { k != vstruct_last(i);) {
struct bkey_s u; struct bkey_s u;
struct bkey tmp; struct bkey tmp;
const char *invalid;
if (btree_err_on(bkey_next(k) > vstruct_last(i), if (btree_err_on(bkey_next(k) > vstruct_last(i),
BTREE_ERR_FIXABLE, c, NULL, b, i, BTREE_ERR_FIXABLE, c, NULL, b, i,
...@@ -804,14 +812,15 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b, ...@@ -804,14 +812,15 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
u = __bkey_disassemble(b, k, &tmp); u = __bkey_disassemble(b, k, &tmp);
invalid = __bch2_bkey_invalid(c, u.s_c, btree_node_type(b)) ?: printbuf_reset(&buf);
(!updated_range ? bch2_bkey_in_btree_node(b, u.s_c) : NULL) ?: if (bset_key_invalid(c, b, u.s_c, updated_range, write, &buf)) {
(write ? bch2_bkey_val_invalid(c, u.s_c) : NULL); printbuf_reset(&buf);
if (invalid) { pr_buf(&buf, "invalid bkey:\n ");
printbuf_reset(&buf1); bch2_bkey_val_to_text(&buf, c, u.s_c);
bch2_bkey_val_to_text(&buf1, c, u.s_c); pr_buf(&buf, " \n");
btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, bset_key_invalid(c, b, u.s_c, updated_range, write, &buf);
"invalid bkey: %s\n%s", invalid, buf1.buf);
btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, "%s", buf.buf);
i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s); i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
memmove_u64s_down(k, bkey_next(k), memmove_u64s_down(k, bkey_next(k),
...@@ -827,16 +836,15 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b, ...@@ -827,16 +836,15 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
if (prev && bkey_iter_cmp(b, prev, k) > 0) { if (prev && bkey_iter_cmp(b, prev, k) > 0) {
struct bkey up = bkey_unpack_key(b, prev); struct bkey up = bkey_unpack_key(b, prev);
printbuf_reset(&buf1); printbuf_reset(&buf);
bch2_bkey_to_text(&buf1, &up); pr_buf(&buf, "keys out of order: ");
printbuf_reset(&buf2); bch2_bkey_to_text(&buf, &up);
bch2_bkey_to_text(&buf2, u.k); pr_buf(&buf, " > ");
bch2_bkey_to_text(&buf, u.k);
bch2_dump_bset(c, b, i, 0); bch2_dump_bset(c, b, i, 0);
if (btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, if (btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, "%s", buf.buf)) {
"keys out of order: %s > %s",
buf1.buf, buf2.buf)) {
i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s); i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
memmove_u64s_down(k, bkey_next(k), memmove_u64s_down(k, bkey_next(k),
(u64 *) vstruct_end(i) - (u64 *) k); (u64 *) vstruct_end(i) - (u64 *) k);
...@@ -848,8 +856,7 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b, ...@@ -848,8 +856,7 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
k = bkey_next(k); k = bkey_next(k);
} }
fsck_err: fsck_err:
printbuf_exit(&buf2); printbuf_exit(&buf);
printbuf_exit(&buf1);
return ret; return ret;
} }
...@@ -868,6 +875,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca, ...@@ -868,6 +875,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
unsigned u64s; unsigned u64s;
unsigned blacklisted_written, nonblacklisted_written = 0; unsigned blacklisted_written, nonblacklisted_written = 0;
unsigned ptr_written = btree_ptr_sectors_written(&b->key); unsigned ptr_written = btree_ptr_sectors_written(&b->key);
struct printbuf buf = PRINTBUF;
int ret, retry_read = 0, write = READ; int ret, retry_read = 0, write = READ;
b->version_ondisk = U16_MAX; b->version_ondisk = U16_MAX;
...@@ -1060,17 +1068,20 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca, ...@@ -1060,17 +1068,20 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
for (k = i->start; k != vstruct_last(i);) { for (k = i->start; k != vstruct_last(i);) {
struct bkey tmp; struct bkey tmp;
struct bkey_s u = __bkey_disassemble(b, k, &tmp); struct bkey_s u = __bkey_disassemble(b, k, &tmp);
const char *invalid = bch2_bkey_val_invalid(c, u.s_c);
if (invalid || printbuf_reset(&buf);
if (bch2_bkey_val_invalid(c, u.s_c, &buf) ||
(bch2_inject_invalid_keys && (bch2_inject_invalid_keys &&
!bversion_cmp(u.k->version, MAX_VERSION))) { !bversion_cmp(u.k->version, MAX_VERSION))) {
struct printbuf buf = PRINTBUF; printbuf_reset(&buf);
pr_buf(&buf, "invalid bkey\n ");
bch2_bkey_val_to_text(&buf, c, u.s_c); bch2_bkey_val_to_text(&buf, c, u.s_c);
btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, pr_buf(&buf, "\n ");
"invalid bkey %s: %s", buf.buf, invalid); bch2_bkey_val_invalid(c, u.s_c, &buf);
printbuf_exit(&buf);
btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, "%s", buf.buf);
btree_keys_account_key_drop(&b->nr, 0, k); btree_keys_account_key_drop(&b->nr, 0, k);
...@@ -1107,6 +1118,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca, ...@@ -1107,6 +1118,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
set_btree_node_need_rewrite(b); set_btree_node_need_rewrite(b);
out: out:
mempool_free(iter, &c->fill_iter); mempool_free(iter, &c->fill_iter);
printbuf_exit(&buf);
return retry_read; return retry_read;
fsck_err: fsck_err:
if (ret == BTREE_RETRY_READ) { if (ret == BTREE_RETRY_READ) {
...@@ -1715,10 +1727,16 @@ static int validate_bset_for_write(struct bch_fs *c, struct btree *b, ...@@ -1715,10 +1727,16 @@ static int validate_bset_for_write(struct bch_fs *c, struct btree *b,
struct bset *i, unsigned sectors) struct bset *i, unsigned sectors)
{ {
unsigned whiteout_u64s = 0; unsigned whiteout_u64s = 0;
struct printbuf buf = PRINTBUF;
int ret; int ret;
if (bch2_bkey_invalid(c, bkey_i_to_s_c(&b->key), BKEY_TYPE_btree)) ret = bch2_bkey_invalid(c, bkey_i_to_s_c(&b->key), BKEY_TYPE_btree, &buf);
return -1;
if (ret)
bch2_fs_inconsistent(c, "invalid btree node key before write: %s", buf.buf);
printbuf_exit(&buf);
if (ret)
return ret;
ret = validate_bset_keys(c, b, i, &whiteout_u64s, WRITE, false) ?: ret = validate_bset_keys(c, b, i, &whiteout_u64s, WRITE, false) ?:
validate_bset(c, NULL, b, i, b->written, sectors, WRITE, false); validate_bset(c, NULL, b, i, b->written, sectors, WRITE, false);
......
...@@ -1176,7 +1176,7 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as, ...@@ -1176,7 +1176,7 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as,
{ {
struct bch_fs *c = as->c; struct bch_fs *c = as->c;
struct bkey_packed *k; struct bkey_packed *k;
const char *invalid; struct printbuf buf = PRINTBUF;
BUG_ON(insert->k.type == KEY_TYPE_btree_ptr_v2 && BUG_ON(insert->k.type == KEY_TYPE_btree_ptr_v2 &&
!btree_ptr_sectors_written(insert)); !btree_ptr_sectors_written(insert));
...@@ -1184,14 +1184,16 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as, ...@@ -1184,14 +1184,16 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as,
if (unlikely(!test_bit(JOURNAL_REPLAY_DONE, &c->journal.flags))) if (unlikely(!test_bit(JOURNAL_REPLAY_DONE, &c->journal.flags)))
bch2_journal_key_overwritten(c, b->c.btree_id, b->c.level, insert->k.p); bch2_journal_key_overwritten(c, b->c.btree_id, b->c.level, insert->k.p);
invalid = bch2_bkey_invalid(c, bkey_i_to_s_c(insert), btree_node_type(b)) ?: if (bch2_bkey_invalid(c, bkey_i_to_s_c(insert), btree_node_type(b), &buf) ?:
bch2_bkey_in_btree_node(b, bkey_i_to_s_c(insert)); bch2_bkey_in_btree_node(b, bkey_i_to_s_c(insert), &buf)) {
if (invalid) { printbuf_reset(&buf);
struct printbuf buf = PRINTBUF; pr_buf(&buf, "inserting invalid bkey\n ");
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(insert)); bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(insert));
bch2_fs_inconsistent(c, "inserting invalid bkey %s: %s", buf.buf, invalid); pr_buf(&buf, "\n ");
printbuf_exit(&buf); bch2_bkey_invalid(c, bkey_i_to_s_c(insert), btree_node_type(b), &buf);
bch2_bkey_in_btree_node(b, bkey_i_to_s_c(insert), &buf);
bch2_fs_inconsistent(c, "%s", buf.buf);
dump_stack(); dump_stack();
} }
...@@ -1211,6 +1213,8 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as, ...@@ -1211,6 +1213,8 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as,
bch2_btree_bset_insert_key(trans, path, b, node_iter, insert); bch2_btree_bset_insert_key(trans, path, b, node_iter, insert);
set_btree_node_dirty_acct(c, b); set_btree_node_dirty_acct(c, b);
set_btree_node_need_write(b); set_btree_node_need_write(b);
printbuf_exit(&buf);
} }
static void static void
......
...@@ -862,23 +862,31 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, ...@@ -862,23 +862,31 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans,
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
struct btree_insert_entry *i; struct btree_insert_entry *i;
struct printbuf buf = PRINTBUF;
int ret, u64s_delta = 0; int ret, u64s_delta = 0;
trans_for_each_update(trans, i) { trans_for_each_update(trans, i) {
const char *invalid = bch2_bkey_invalid(c, if (bch2_bkey_invalid(c, bkey_i_to_s_c(i->k), i->bkey_type, &buf)) {
bkey_i_to_s_c(i->k), i->bkey_type); printbuf_reset(&buf);
if (invalid) { pr_buf(&buf, "invalid bkey on insert from %s -> %ps",
struct printbuf buf = PRINTBUF; trans->fn, (void *) i->ip_allocated);
pr_newline(&buf);
pr_indent_push(&buf, 2);
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(i->k)); bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(i->k));
bch2_fs_fatal_error(c, "invalid bkey %s on insert from %s -> %ps: %s\n", pr_newline(&buf);
buf.buf, trans->fn, (void *) i->ip_allocated, invalid);
bch2_bkey_invalid(c, bkey_i_to_s_c(i->k), i->bkey_type, &buf);
bch2_fs_fatal_error(c, "%s", buf.buf);
printbuf_exit(&buf); printbuf_exit(&buf);
return -EINVAL; return -EINVAL;
} }
btree_insert_entry_checks(trans, i); btree_insert_entry_checks(trans, i);
} }
printbuf_exit(&buf);
trans_for_each_update(trans, i) { trans_for_each_update(trans, i) {
if (i->cached) if (i->cached)
continue; continue;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define _BUCKETS_H #define _BUCKETS_H
#include "buckets_types.h" #include "buckets_types.h"
#include "extents.h"
#include "super.h" #include "super.h"
#define for_each_bucket(_b, _buckets) \ #define for_each_bucket(_b, _buckets) \
...@@ -83,8 +84,7 @@ static inline struct bucket *PTR_GC_BUCKET(struct bch_dev *ca, ...@@ -83,8 +84,7 @@ static inline struct bucket *PTR_GC_BUCKET(struct bch_dev *ca,
static inline enum bch_data_type ptr_data_type(const struct bkey *k, static inline enum bch_data_type ptr_data_type(const struct bkey *k,
const struct bch_extent_ptr *ptr) const struct bch_extent_ptr *ptr)
{ {
if (k->type == KEY_TYPE_btree_ptr || if (bkey_is_btree_ptr(k))
k->type == KEY_TYPE_btree_ptr_v2)
return BCH_DATA_btree; return BCH_DATA_btree;
return ptr->cached ? BCH_DATA_cached : BCH_DATA_user; return ptr->cached ? BCH_DATA_cached : BCH_DATA_user;
......
...@@ -83,38 +83,58 @@ const struct bch_hash_desc bch2_dirent_hash_desc = { ...@@ -83,38 +83,58 @@ const struct bch_hash_desc bch2_dirent_hash_desc = {
.is_visible = dirent_is_visible, .is_visible = dirent_is_visible,
}; };
const char *bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
unsigned len; unsigned len;
if (bkey_val_bytes(k.k) < sizeof(struct bch_dirent)) if (bkey_val_bytes(k.k) < sizeof(struct bch_dirent)) {
return "value too small"; pr_buf(err, "incorrect value size (%zu < %zu)",
bkey_val_bytes(k.k), sizeof(*d.v));
return -EINVAL;
}
len = bch2_dirent_name_bytes(d); len = bch2_dirent_name_bytes(d);
if (!len) if (!len) {
return "empty name"; pr_buf(err, "empty name");
return -EINVAL;
}
if (bkey_val_u64s(k.k) > dirent_val_u64s(len)) if (bkey_val_u64s(k.k) > dirent_val_u64s(len)) {
return "value too big"; pr_buf(err, "value too big (%zu > %u)",
bkey_val_u64s(k.k),dirent_val_u64s(len));
return -EINVAL;
}
if (len > BCH_NAME_MAX) if (len > BCH_NAME_MAX) {
return "dirent name too big"; pr_buf(err, "dirent name too big (%u > %lu)",
len, BCH_NAME_MAX);
return -EINVAL;
}
if (len == 1 && !memcmp(d.v->d_name, ".", 1)) if (len == 1 && !memcmp(d.v->d_name, ".", 1)) {
return "invalid name"; pr_buf(err, "invalid name");
return -EINVAL;
}
if (len == 2 && !memcmp(d.v->d_name, "..", 2)) if (len == 2 && !memcmp(d.v->d_name, "..", 2)) {
return "invalid name"; pr_buf(err, "invalid name");
return -EINVAL;
}
if (memchr(d.v->d_name, '/', len)) if (memchr(d.v->d_name, '/', len)) {
return "invalid name"; pr_buf(err, "invalid name");
return -EINVAL;
}
if (d.v->d_type != DT_SUBVOL && if (d.v->d_type != DT_SUBVOL &&
le64_to_cpu(d.v->d_inum) == d.k->p.inode) le64_to_cpu(d.v->d_inum) == d.k->p.inode) {
return "dirent points to own directory"; pr_buf(err, "dirent points to own directory");
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c, void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
extern const struct bch_hash_desc bch2_dirent_hash_desc; extern const struct bch_hash_desc bch2_dirent_hash_desc;
const char *bch2_dirent_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_dirent_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_dirent_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_dirent_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_dirent (struct bkey_ops) { \ #define bch2_bkey_ops_dirent (struct bkey_ops) { \
......
...@@ -102,24 +102,34 @@ struct ec_bio { ...@@ -102,24 +102,34 @@ struct ec_bio {
/* Stripes btree keys: */ /* Stripes btree keys: */
const char *bch2_stripe_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_stripe_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
const struct bch_stripe *s = bkey_s_c_to_stripe(k).v; const struct bch_stripe *s = bkey_s_c_to_stripe(k).v;
if (!bkey_cmp(k.k->p, POS_MIN)) if (!bkey_cmp(k.k->p, POS_MIN)) {
return "stripe at pos 0"; pr_buf(err, "stripe at POS_MIN");
return -EINVAL;
}
if (k.k->p.inode) if (k.k->p.inode) {
return "invalid stripe key"; pr_buf(err, "nonzero inode field");
return -EINVAL;
}
if (bkey_val_bytes(k.k) < sizeof(*s)) if (bkey_val_bytes(k.k) < sizeof(*s)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu < %zu)",
bkey_val_bytes(k.k), sizeof(*s));
return -EINVAL;
}
if (bkey_val_bytes(k.k) < sizeof(*s) || if (bkey_val_u64s(k.k) < stripe_val_u64s(s)) {
bkey_val_u64s(k.k) < stripe_val_u64s(s)) pr_buf(err, "incorrect value size (%zu < %u)",
return "incorrect value size"; bkey_val_u64s(k.k), stripe_val_u64s(s));
return -EINVAL;
}
return bch2_bkey_ptrs_invalid(c, k); return bch2_bkey_ptrs_invalid(c, k, err);
} }
void bch2_stripe_to_text(struct printbuf *out, struct bch_fs *c, void bch2_stripe_to_text(struct printbuf *out, struct bch_fs *c,
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
#include "buckets_types.h" #include "buckets_types.h"
#include "keylist_types.h" #include "keylist_types.h"
const char *bch2_stripe_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_stripe_invalid(const struct bch_fs *, struct bkey_s_c,
struct printbuf *);
void bch2_stripe_to_text(struct printbuf *, struct bch_fs *, void bch2_stripe_to_text(struct printbuf *, struct bch_fs *,
struct bkey_s_c); struct bkey_s_c);
......
...@@ -155,12 +155,16 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k, ...@@ -155,12 +155,16 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
/* KEY_TYPE_btree_ptr: */ /* KEY_TYPE_btree_ptr: */
const char *bch2_btree_ptr_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_btree_ptr_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
if (bkey_val_u64s(k.k) > BCH_REPLICAS_MAX) if (bkey_val_u64s(k.k) > BCH_REPLICAS_MAX) {
return "value too big"; pr_buf(err, "value too big (%zu > %u)",
bkey_val_u64s(k.k), BCH_REPLICAS_MAX);
return -EINVAL;
}
return bch2_bkey_ptrs_invalid(c, k); return bch2_bkey_ptrs_invalid(c, k, err);
} }
void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c, void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c,
...@@ -169,21 +173,31 @@ void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c, ...@@ -169,21 +173,31 @@ void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c,
bch2_bkey_ptrs_to_text(out, c, k); bch2_bkey_ptrs_to_text(out, c, k);
} }
const char *bch2_btree_ptr_v2_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_btree_ptr_v2_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_s_c_btree_ptr_v2 bp = bkey_s_c_to_btree_ptr_v2(k); struct bkey_s_c_btree_ptr_v2 bp = bkey_s_c_to_btree_ptr_v2(k);
if (bkey_val_bytes(k.k) <= sizeof(*bp.v)) if (bkey_val_bytes(k.k) <= sizeof(*bp.v)) {
return "value too small"; pr_buf(err, "value too small (%zu <= %zu)",
bkey_val_bytes(k.k), sizeof(*bp.v));
return -EINVAL;
}
if (bkey_val_u64s(k.k) > BKEY_BTREE_PTR_VAL_U64s_MAX) if (bkey_val_u64s(k.k) > BKEY_BTREE_PTR_VAL_U64s_MAX) {
return "value too big"; pr_buf(err, "value too big (%zu > %zu)",
bkey_val_u64s(k.k), BKEY_BTREE_PTR_VAL_U64s_MAX);
return -EINVAL;
}
if (c->sb.version < bcachefs_metadata_version_snapshot && if (c->sb.version < bcachefs_metadata_version_snapshot &&
bp.v->min_key.snapshot) bp.v->min_key.snapshot) {
return "invalid min_key.snapshot"; pr_buf(err, "invalid min_key.snapshot (%u != 0)",
bp.v->min_key.snapshot);
return -EINVAL;
}
return bch2_bkey_ptrs_invalid(c, k); return bch2_bkey_ptrs_invalid(c, k, err);
} }
void bch2_btree_ptr_v2_to_text(struct printbuf *out, struct bch_fs *c, void bch2_btree_ptr_v2_to_text(struct printbuf *out, struct bch_fs *c,
...@@ -219,17 +233,6 @@ void bch2_btree_ptr_v2_compat(enum btree_id btree_id, unsigned version, ...@@ -219,17 +233,6 @@ void bch2_btree_ptr_v2_compat(enum btree_id btree_id, unsigned version,
/* KEY_TYPE_extent: */ /* KEY_TYPE_extent: */
const char *bch2_extent_invalid(const struct bch_fs *c, struct bkey_s_c k)
{
return bch2_bkey_ptrs_invalid(c, k);
}
void bch2_extent_to_text(struct printbuf *out, struct bch_fs *c,
struct bkey_s_c k)
{
bch2_bkey_ptrs_to_text(out, c, k);
}
bool bch2_extent_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r) bool bch2_extent_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r)
{ {
struct bkey_ptrs l_ptrs = bch2_bkey_ptrs(l); struct bkey_ptrs l_ptrs = bch2_bkey_ptrs(l);
...@@ -362,17 +365,24 @@ bool bch2_extent_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r) ...@@ -362,17 +365,24 @@ bool bch2_extent_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r)
/* KEY_TYPE_reservation: */ /* KEY_TYPE_reservation: */
const char *bch2_reservation_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_reservation_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_s_c_reservation r = bkey_s_c_to_reservation(k); struct bkey_s_c_reservation r = bkey_s_c_to_reservation(k);
if (bkey_val_bytes(k.k) != sizeof(struct bch_reservation)) if (bkey_val_bytes(k.k) != sizeof(struct bch_reservation)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu != %zu)",
bkey_val_bytes(k.k), sizeof(*r.v));
return -EINVAL;
}
if (!r.v->nr_replicas || r.v->nr_replicas > BCH_REPLICAS_MAX) if (!r.v->nr_replicas || r.v->nr_replicas > BCH_REPLICAS_MAX) {
return "invalid nr_replicas"; pr_buf(err, "invalid nr_replicas (%u)",
r.v->nr_replicas);
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_reservation_to_text(struct printbuf *out, struct bch_fs *c, void bch2_reservation_to_text(struct printbuf *out, struct bch_fs *c,
...@@ -1000,69 +1010,86 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c, ...@@ -1000,69 +1010,86 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
} }
} }
static const char *extent_ptr_invalid(const struct bch_fs *c, static int extent_ptr_invalid(const struct bch_fs *c,
struct bkey_s_c k, struct bkey_s_c k,
const struct bch_extent_ptr *ptr, const struct bch_extent_ptr *ptr,
unsigned size_ondisk, unsigned size_ondisk,
bool metadata) bool metadata,
struct printbuf *err)
{ {
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
const struct bch_extent_ptr *ptr2; const struct bch_extent_ptr *ptr2;
u64 bucket;
u32 bucket_offset;
struct bch_dev *ca; struct bch_dev *ca;
if (!bch2_dev_exists2(c, ptr->dev)) if (!bch2_dev_exists2(c, ptr->dev)) {
return "pointer to invalid device"; pr_buf(err, "pointer to invalid device (%u)", ptr->dev);
return -EINVAL;
}
ca = bch_dev_bkey_exists(c, ptr->dev); ca = bch_dev_bkey_exists(c, ptr->dev);
if (!ca)
return "pointer to invalid device";
bkey_for_each_ptr(ptrs, ptr2) bkey_for_each_ptr(ptrs, ptr2)
if (ptr != ptr2 && ptr->dev == ptr2->dev) if (ptr != ptr2 && ptr->dev == ptr2->dev) {
return "multiple pointers to same device"; pr_buf(err, "multiple pointers to same device (%u)", ptr->dev);
return -EINVAL;
}
if (ptr->offset + size_ondisk > bucket_to_sector(ca, ca->mi.nbuckets)) bucket = sector_to_bucket_and_offset(ca, ptr->offset, &bucket_offset);
return "offset past end of device";
if (ptr->offset < bucket_to_sector(ca, ca->mi.first_bucket)) if (bucket >= ca->mi.nbuckets) {
return "offset before first bucket"; pr_buf(err, "pointer past last bucket (%llu > %llu)",
bucket, ca->mi.nbuckets);
return -EINVAL;
}
if (bucket_remainder(ca, ptr->offset) + if (ptr->offset < bucket_to_sector(ca, ca->mi.first_bucket)) {
size_ondisk > ca->mi.bucket_size) pr_buf(err, "pointer before first bucket (%llu < %u)",
return "spans multiple buckets"; bucket, ca->mi.first_bucket);
return -EINVAL;
}
return NULL; if (bucket_offset + size_ondisk > ca->mi.bucket_size) {
pr_buf(err, "pointer spans multiple buckets (%u + %u > %u)",
bucket_offset, size_ondisk, ca->mi.bucket_size);
return -EINVAL;
}
return 0;
} }
const char *bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
struct bch_devs_list devs;
const union bch_extent_entry *entry; const union bch_extent_entry *entry;
struct bch_extent_crc_unpacked crc; struct bch_extent_crc_unpacked crc;
unsigned size_ondisk = k.k->size; unsigned size_ondisk = k.k->size;
const char *reason;
unsigned nonce = UINT_MAX; unsigned nonce = UINT_MAX;
unsigned i; int ret;
if (k.k->type == KEY_TYPE_btree_ptr || if (bkey_is_btree_ptr(k.k))
k.k->type == KEY_TYPE_btree_ptr_v2)
size_ondisk = btree_sectors(c); size_ondisk = btree_sectors(c);
bkey_extent_entry_for_each(ptrs, entry) { bkey_extent_entry_for_each(ptrs, entry) {
if (__extent_entry_type(entry) >= BCH_EXTENT_ENTRY_MAX) if (__extent_entry_type(entry) >= BCH_EXTENT_ENTRY_MAX) {
return "invalid extent entry type"; pr_buf(err, "invalid extent entry type (got %u, max %u)",
__extent_entry_type(entry), BCH_EXTENT_ENTRY_MAX);
return -EINVAL;
}
if (k.k->type == KEY_TYPE_btree_ptr && if (bkey_is_btree_ptr(k.k) &&
!extent_entry_is_ptr(entry)) !extent_entry_is_ptr(entry)) {
return "has non ptr field"; pr_buf(err, "has non ptr field");
return -EINVAL;
}
switch (extent_entry_type(entry)) { switch (extent_entry_type(entry)) {
case BCH_EXTENT_ENTRY_ptr: case BCH_EXTENT_ENTRY_ptr:
reason = extent_ptr_invalid(c, k, &entry->ptr, ret = extent_ptr_invalid(c, k, &entry->ptr, size_ondisk,
size_ondisk, false); false, err);
if (reason) if (ret)
return reason; return ret;
break; break;
case BCH_EXTENT_ENTRY_crc32: case BCH_EXTENT_ENTRY_crc32:
case BCH_EXTENT_ENTRY_crc64: case BCH_EXTENT_ENTRY_crc64:
...@@ -1070,22 +1097,30 @@ const char *bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k) ...@@ -1070,22 +1097,30 @@ const char *bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k)
crc = bch2_extent_crc_unpack(k.k, entry_to_crc(entry)); crc = bch2_extent_crc_unpack(k.k, entry_to_crc(entry));
if (crc.offset + crc.live_size > if (crc.offset + crc.live_size >
crc.uncompressed_size) crc.uncompressed_size) {
return "checksum offset + key size > uncompressed size"; pr_buf(err, "checksum offset + key size > uncompressed size");
return -EINVAL;
}
size_ondisk = crc.compressed_size; size_ondisk = crc.compressed_size;
if (!bch2_checksum_type_valid(c, crc.csum_type)) if (!bch2_checksum_type_valid(c, crc.csum_type)) {
return "invalid checksum type"; pr_buf(err, "invalid checksum type");
return -EINVAL;
}
if (crc.compression_type >= BCH_COMPRESSION_TYPE_NR) if (crc.compression_type >= BCH_COMPRESSION_TYPE_NR) {
return "invalid compression type"; pr_buf(err, "invalid compression type");
return -EINVAL;
}
if (bch2_csum_type_is_encryption(crc.csum_type)) { if (bch2_csum_type_is_encryption(crc.csum_type)) {
if (nonce == UINT_MAX) if (nonce == UINT_MAX)
nonce = crc.offset + crc.nonce; nonce = crc.offset + crc.nonce;
else if (nonce != crc.offset + crc.nonce) else if (nonce != crc.offset + crc.nonce) {
return "incorrect nonce"; pr_buf(err, "incorrect nonce");
return -EINVAL;
}
} }
break; break;
case BCH_EXTENT_ENTRY_stripe_ptr: case BCH_EXTENT_ENTRY_stripe_ptr:
...@@ -1093,13 +1128,7 @@ const char *bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k) ...@@ -1093,13 +1128,7 @@ const char *bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k)
} }
} }
devs = bch2_bkey_devs(k); return 0;
bubble_sort(devs.devs, devs.nr, u8_cmp);
for (i = 0; i + 1 < devs.nr; i++)
if (devs.devs[i] == devs.devs[i + 1])
return "multiple ptrs to same device";
return NULL;
} }
void bch2_ptr_swab(struct bkey_s k) void bch2_ptr_swab(struct bkey_s k)
......
...@@ -367,13 +367,12 @@ int bch2_bkey_pick_read_device(struct bch_fs *, struct bkey_s_c, ...@@ -367,13 +367,12 @@ int bch2_bkey_pick_read_device(struct bch_fs *, struct bkey_s_c,
/* KEY_TYPE_btree_ptr: */ /* KEY_TYPE_btree_ptr: */
const char *bch2_btree_ptr_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_btree_ptr_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_btree_ptr_to_text(struct printbuf *, struct bch_fs *, void bch2_btree_ptr_to_text(struct printbuf *, struct bch_fs *,
struct bkey_s_c); struct bkey_s_c);
const char *bch2_btree_ptr_v2_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_btree_ptr_v2_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_btree_ptr_v2_to_text(struct printbuf *, struct bch_fs *, void bch2_btree_ptr_v2_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
struct bkey_s_c);
void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned, void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned,
int, struct bkey_s); int, struct bkey_s);
...@@ -396,13 +395,11 @@ void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned, ...@@ -396,13 +395,11 @@ void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned,
/* KEY_TYPE_extent: */ /* KEY_TYPE_extent: */
const char *bch2_extent_invalid(const struct bch_fs *, struct bkey_s_c);
void bch2_extent_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
bool bch2_extent_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c); bool bch2_extent_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
#define bch2_bkey_ops_extent (struct bkey_ops) { \ #define bch2_bkey_ops_extent (struct bkey_ops) { \
.key_invalid = bch2_extent_invalid, \ .key_invalid = bch2_bkey_ptrs_invalid, \
.val_to_text = bch2_extent_to_text, \ .val_to_text = bch2_bkey_ptrs_to_text, \
.swab = bch2_ptr_swab, \ .swab = bch2_ptr_swab, \
.key_normalize = bch2_extent_normalize, \ .key_normalize = bch2_extent_normalize, \
.key_merge = bch2_extent_merge, \ .key_merge = bch2_extent_merge, \
...@@ -412,7 +409,7 @@ bool bch2_extent_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c); ...@@ -412,7 +409,7 @@ bool bch2_extent_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
/* KEY_TYPE_reservation: */ /* KEY_TYPE_reservation: */
const char *bch2_reservation_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_reservation_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_reservation_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); 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); bool bch2_reservation_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
...@@ -618,7 +615,7 @@ bool bch2_bkey_matches_ptr(struct bch_fs *, struct bkey_s_c, ...@@ -618,7 +615,7 @@ bool bch2_bkey_matches_ptr(struct bch_fs *, struct bkey_s_c,
bool bch2_extent_normalize(struct bch_fs *, struct bkey_s); bool bch2_extent_normalize(struct bch_fs *, struct bkey_s);
void bch2_bkey_ptrs_to_text(struct printbuf *, struct bch_fs *, void bch2_bkey_ptrs_to_text(struct printbuf *, struct bch_fs *,
struct bkey_s_c); struct bkey_s_c);
const char *bch2_bkey_ptrs_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_bkey_ptrs_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_ptr_swab(struct bkey_s); void bch2_ptr_swab(struct bkey_s);
......
...@@ -293,76 +293,89 @@ int bch2_inode_write(struct btree_trans *trans, ...@@ -293,76 +293,89 @@ int bch2_inode_write(struct btree_trans *trans,
return bch2_trans_update(trans, iter, &inode_p->inode.k_i, 0); return bch2_trans_update(trans, iter, &inode_p->inode.k_i, 0);
} }
const char *bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k) static int __bch2_inode_invalid(struct bkey_s_c k, struct printbuf *err)
{ {
struct bkey_s_c_inode inode = bkey_s_c_to_inode(k);
struct bch_inode_unpacked unpacked; struct bch_inode_unpacked unpacked;
if (k.k->p.inode) if (k.k->p.inode) {
return "nonzero k.p.inode"; pr_buf(err, "nonzero k.p.inode");
return -EINVAL;
if (bkey_val_bytes(k.k) < sizeof(struct bch_inode)) }
return "incorrect value size";
if (k.k->p.offset < BLOCKDEV_INODE_MAX)
return "fs inode in blockdev range";
if (INODE_STR_HASH(inode.v) >= BCH_STR_HASH_NR) if (k.k->p.offset < BLOCKDEV_INODE_MAX) {
return "invalid str hash type"; pr_buf(err, "fs inode in blockdev range");
return -EINVAL;
}
if (bch2_inode_unpack(k, &unpacked)) if (bch2_inode_unpack(k, &unpacked)){
return "invalid variable length fields"; pr_buf(err, "invalid variable length fields");
return -EINVAL;
}
if (unpacked.bi_data_checksum >= BCH_CSUM_OPT_NR + 1) if (unpacked.bi_data_checksum >= BCH_CSUM_OPT_NR + 1) {
return "invalid data checksum type"; pr_buf(err, "invalid data checksum type (%u >= %u",
unpacked.bi_data_checksum, BCH_CSUM_OPT_NR + 1);
return -EINVAL;
}
if (unpacked.bi_compression >= BCH_COMPRESSION_OPT_NR + 1) if (unpacked.bi_compression >= BCH_COMPRESSION_OPT_NR + 1) {
return "invalid data checksum type"; pr_buf(err, "invalid data checksum type (%u >= %u)",
unpacked.bi_compression, BCH_COMPRESSION_OPT_NR + 1);
return -EINVAL;
}
if ((unpacked.bi_flags & BCH_INODE_UNLINKED) && if ((unpacked.bi_flags & BCH_INODE_UNLINKED) &&
unpacked.bi_nlink != 0) unpacked.bi_nlink != 0) {
return "flagged as unlinked but bi_nlink != 0"; pr_buf(err, "flagged as unlinked but bi_nlink != 0");
return -EINVAL;
}
if (unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode)) if (unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode)) {
return "subvolume root but not a directory"; pr_buf(err, "subvolume root but not a directory");
return -EINVAL;
}
return NULL; return 0;
} }
const char *bch2_inode_v2_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_s_c_inode_v2 inode = bkey_s_c_to_inode_v2(k); struct bkey_s_c_inode inode = bkey_s_c_to_inode(k);
struct bch_inode_unpacked unpacked;
if (k.k->p.inode)
return "nonzero k.p.inode";
if (bkey_val_bytes(k.k) < sizeof(struct bch_inode))
return "incorrect value size";
if (k.k->p.offset < BLOCKDEV_INODE_MAX)
return "fs inode in blockdev range";
if (INODEv2_STR_HASH(inode.v) >= BCH_STR_HASH_NR) if (bkey_val_bytes(k.k) < sizeof(*inode.v)) {
return "invalid str hash type"; pr_buf(err, "incorrect value size (%zu < %zu)",
bkey_val_bytes(k.k), sizeof(*inode.v));
return -EINVAL;
}
if (bch2_inode_unpack(k, &unpacked)) if (INODE_STR_HASH(inode.v) >= BCH_STR_HASH_NR) {
return "invalid variable length fields"; pr_buf(err, "invalid str hash type (%llu >= %u)",
INODE_STR_HASH(inode.v), BCH_STR_HASH_NR);
return -EINVAL;
}
if (unpacked.bi_data_checksum >= BCH_CSUM_OPT_NR + 1) return __bch2_inode_invalid(k, err);
return "invalid data checksum type"; }
if (unpacked.bi_compression >= BCH_COMPRESSION_OPT_NR + 1) int bch2_inode_v2_invalid(const struct bch_fs *c, struct bkey_s_c k,
return "invalid data checksum type"; struct printbuf *err)
{
struct bkey_s_c_inode_v2 inode = bkey_s_c_to_inode_v2(k);
if ((unpacked.bi_flags & BCH_INODE_UNLINKED) && if (bkey_val_bytes(k.k) < sizeof(*inode.v)) {
unpacked.bi_nlink != 0) pr_buf(err, "incorrect value size (%zu < %zu)",
return "flagged as unlinked but bi_nlink != 0"; bkey_val_bytes(k.k), sizeof(*inode.v));
return -EINVAL;
}
if (unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode)) if (INODEv2_STR_HASH(inode.v) >= BCH_STR_HASH_NR) {
return "subvolume root but not a directory"; pr_buf(err, "invalid str hash type (%llu >= %u)",
INODEv2_STR_HASH(inode.v), BCH_STR_HASH_NR);
return -EINVAL;
}
return NULL; return __bch2_inode_invalid(k, err);
} }
static void __bch2_inode_unpacked_to_text(struct printbuf *out, struct bch_inode_unpacked *inode) static void __bch2_inode_unpacked_to_text(struct printbuf *out, struct bch_inode_unpacked *inode)
...@@ -396,16 +409,21 @@ void bch2_inode_to_text(struct printbuf *out, struct bch_fs *c, ...@@ -396,16 +409,21 @@ void bch2_inode_to_text(struct printbuf *out, struct bch_fs *c,
__bch2_inode_unpacked_to_text(out, &inode); __bch2_inode_unpacked_to_text(out, &inode);
} }
const char *bch2_inode_generation_invalid(const struct bch_fs *c, int bch2_inode_generation_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct bkey_s_c k) struct printbuf *err)
{ {
if (k.k->p.inode) if (k.k->p.inode) {
return "nonzero k.p.inode"; pr_buf(err, "nonzero k.p.inode");
return -EINVAL;
}
if (bkey_val_bytes(k.k) != sizeof(struct bch_inode_generation)) if (bkey_val_bytes(k.k) != sizeof(struct bch_inode_generation)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu != %zu)",
bkey_val_bytes(k.k), sizeof(struct bch_inode_generation));
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_inode_generation_to_text(struct printbuf *out, struct bch_fs *c, void bch2_inode_generation_to_text(struct printbuf *out, struct bch_fs *c,
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
extern const char * const bch2_inode_opts[]; extern const char * const bch2_inode_opts[];
const char *bch2_inode_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_inode_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
const char *bch2_inode_v2_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_inode_v2_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_inode_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_inode_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_inode (struct bkey_ops) { \ #define bch2_bkey_ops_inode (struct bkey_ops) { \
...@@ -30,10 +30,8 @@ static inline bool bkey_is_inode(const struct bkey *k) ...@@ -30,10 +30,8 @@ static inline bool bkey_is_inode(const struct bkey *k)
k->type == KEY_TYPE_inode_v2; k->type == KEY_TYPE_inode_v2;
} }
const char *bch2_inode_generation_invalid(const struct bch_fs *, int bch2_inode_generation_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
struct bkey_s_c); void bch2_inode_generation_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
void bch2_inode_generation_to_text(struct printbuf *, struct bch_fs *,
struct bkey_s_c);
#define bch2_bkey_ops_inode_generation (struct bkey_ops) { \ #define bch2_bkey_ops_inode_generation (struct bkey_ops) { \
.key_invalid = bch2_inode_generation_invalid, \ .key_invalid = bch2_inode_generation_invalid, \
......
...@@ -209,7 +209,7 @@ static int journal_validate_key(struct bch_fs *c, const char *where, ...@@ -209,7 +209,7 @@ static int journal_validate_key(struct bch_fs *c, const char *where,
unsigned version, int big_endian, int write) unsigned version, int big_endian, int write)
{ {
void *next = vstruct_next(entry); void *next = vstruct_next(entry);
const char *invalid; struct printbuf buf = PRINTBUF;
int ret = 0; int ret = 0;
if (journal_entry_err_on(!k->k.u64s, c, if (journal_entry_err_on(!k->k.u64s, c,
...@@ -249,22 +249,28 @@ static int journal_validate_key(struct bch_fs *c, const char *where, ...@@ -249,22 +249,28 @@ static int journal_validate_key(struct bch_fs *c, const char *where,
bch2_bkey_compat(level, btree_id, version, big_endian, bch2_bkey_compat(level, btree_id, version, big_endian,
write, NULL, bkey_to_packed(k)); write, NULL, bkey_to_packed(k));
invalid = bch2_bkey_invalid(c, bkey_i_to_s_c(k), if (bch2_bkey_invalid(c, bkey_i_to_s_c(k),
__btree_node_type(level, btree_id)); __btree_node_type(level, btree_id), &buf)) {
if (invalid) { printbuf_reset(&buf);
struct printbuf buf = PRINTBUF; pr_buf(&buf, "invalid %s in %s entry offset %zi/%u:",
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
mustfix_fsck_err(c, "invalid %s in %s entry offset %zi/%u: %s\n%s",
type, where, type, where,
(u64 *) k - entry->_data, (u64 *) k - entry->_data,
le16_to_cpu(entry->u64s), le16_to_cpu(entry->u64s));
invalid, buf.buf); pr_newline(&buf);
printbuf_exit(&buf); pr_indent_push(&buf, 2);
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
pr_newline(&buf);
bch2_bkey_invalid(c, bkey_i_to_s_c(k),
__btree_node_type(level, btree_id), &buf);
mustfix_fsck_err(c, "%s", buf.buf);
le16_add_cpu(&entry->u64s, -((u16) k->k.u64s)); le16_add_cpu(&entry->u64s, -((u16) k->k.u64s));
memmove(k, bkey_next(k), next - (void *) bkey_next(k)); memmove(k, bkey_next(k), next - (void *) bkey_next(k));
journal_entry_null_range(vstruct_next(entry), next); journal_entry_null_range(vstruct_next(entry), next);
printbuf_exit(&buf);
return FSCK_DELETED_KEY; return FSCK_DELETED_KEY;
} }
...@@ -272,6 +278,7 @@ static int journal_validate_key(struct bch_fs *c, const char *where, ...@@ -272,6 +278,7 @@ static int journal_validate_key(struct bch_fs *c, const char *where,
bch2_bkey_compat(level, btree_id, version, big_endian, bch2_bkey_compat(level, btree_id, version, big_endian,
write, NULL, bkey_to_packed(k)); write, NULL, bkey_to_packed(k));
fsck_err: fsck_err:
printbuf_exit(&buf);
return ret; return ret;
} }
......
...@@ -8,14 +8,18 @@ ...@@ -8,14 +8,18 @@
#include "lru.h" #include "lru.h"
#include "recovery.h" #include "recovery.h"
const char *bch2_lru_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_lru_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
const struct bch_lru *lru = bkey_s_c_to_lru(k).v; const struct bch_lru *lru = bkey_s_c_to_lru(k).v;
if (bkey_val_bytes(k.k) < sizeof(*lru)) if (bkey_val_bytes(k.k) < sizeof(*lru)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu < %zu)",
bkey_val_bytes(k.k), sizeof(*lru));
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_lru_to_text(struct printbuf *out, struct bch_fs *c, void bch2_lru_to_text(struct printbuf *out, struct bch_fs *c,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#ifndef _BCACHEFS_LRU_H #ifndef _BCACHEFS_LRU_H
#define _BCACHEFS_LRU_H #define _BCACHEFS_LRU_H
const char *bch2_lru_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_lru_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_lru_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_lru_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_lru (struct bkey_ops) { \ #define bch2_bkey_ops_lru (struct bkey_ops) { \
......
...@@ -57,15 +57,22 @@ const struct bch_sb_field_ops bch_sb_field_ops_quota = { ...@@ -57,15 +57,22 @@ const struct bch_sb_field_ops bch_sb_field_ops_quota = {
.to_text = bch2_sb_quota_to_text, .to_text = bch2_sb_quota_to_text,
}; };
const char *bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
if (k.k->p.inode >= QTYP_NR) if (k.k->p.inode >= QTYP_NR) {
return "invalid quota type"; pr_buf(err, "invalid quota type (%llu >= %u)",
k.k->p.inode, QTYP_NR);
return -EINVAL;
}
if (bkey_val_bytes(k.k) != sizeof(struct bch_quota)) if (bkey_val_bytes(k.k) != sizeof(struct bch_quota)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu != %zu)",
bkey_val_bytes(k.k), sizeof(struct bch_quota));
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c, void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
extern const struct bch_sb_field_ops bch_sb_field_ops_quota; extern const struct bch_sb_field_ops bch_sb_field_ops_quota;
const char *bch2_quota_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_quota_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_quota_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_quota_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_quota (struct bkey_ops) { \ #define bch2_bkey_ops_quota (struct bkey_ops) { \
......
...@@ -25,18 +25,25 @@ static inline unsigned bkey_type_to_indirect(const struct bkey *k) ...@@ -25,18 +25,25 @@ static inline unsigned bkey_type_to_indirect(const struct bkey *k)
/* reflink pointers */ /* reflink pointers */
const char *bch2_reflink_p_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_reflink_p_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_s_c_reflink_p p = bkey_s_c_to_reflink_p(k); struct bkey_s_c_reflink_p p = bkey_s_c_to_reflink_p(k);
if (bkey_val_bytes(p.k) != sizeof(*p.v)) if (bkey_val_bytes(p.k) != sizeof(*p.v)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu != %zu)",
bkey_val_bytes(p.k), sizeof(*p.v));
return -EINVAL;
}
if (c->sb.version >= bcachefs_metadata_version_reflink_p_fix && if (c->sb.version >= bcachefs_metadata_version_reflink_p_fix &&
le64_to_cpu(p.v->idx) < le32_to_cpu(p.v->front_pad)) le64_to_cpu(p.v->idx) < le32_to_cpu(p.v->front_pad)) {
return "idx < front_pad"; pr_buf(err, "idx < front_pad (%llu < %u)",
le64_to_cpu(p.v->idx), le32_to_cpu(p.v->front_pad));
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_reflink_p_to_text(struct printbuf *out, struct bch_fs *c, void bch2_reflink_p_to_text(struct printbuf *out, struct bch_fs *c,
...@@ -70,14 +77,18 @@ bool bch2_reflink_p_merge(struct bch_fs *c, struct bkey_s _l, struct bkey_s_c _r ...@@ -70,14 +77,18 @@ bool bch2_reflink_p_merge(struct bch_fs *c, struct bkey_s _l, struct bkey_s_c _r
/* indirect extents */ /* indirect extents */
const char *bch2_reflink_v_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_reflink_v_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_s_c_reflink_v r = bkey_s_c_to_reflink_v(k); struct bkey_s_c_reflink_v r = bkey_s_c_to_reflink_v(k);
if (bkey_val_bytes(r.k) < sizeof(*r.v)) if (bkey_val_bytes(r.k) < sizeof(*r.v)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu < %zu)",
bkey_val_bytes(r.k), sizeof(*r.v));
return -EINVAL;
}
return bch2_bkey_ptrs_invalid(c, k); return bch2_bkey_ptrs_invalid(c, k, err);
} }
void bch2_reflink_v_to_text(struct printbuf *out, struct bch_fs *c, void bch2_reflink_v_to_text(struct printbuf *out, struct bch_fs *c,
...@@ -118,12 +129,16 @@ int bch2_trans_mark_reflink_v(struct btree_trans *trans, ...@@ -118,12 +129,16 @@ int bch2_trans_mark_reflink_v(struct btree_trans *trans,
/* indirect inline data */ /* indirect inline data */
const char *bch2_indirect_inline_data_invalid(const struct bch_fs *c, int bch2_indirect_inline_data_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct bkey_s_c k) struct printbuf *err)
{ {
if (bkey_val_bytes(k.k) < sizeof(struct bch_indirect_inline_data)) if (bkey_val_bytes(k.k) < sizeof(struct bch_indirect_inline_data)) {
return "incorrect value size"; pr_buf(err, "incorrect value size (%zu < %zu)",
return NULL; bkey_val_bytes(k.k), sizeof(struct bch_indirect_inline_data));
return -EINVAL;
}
return 0;
} }
void bch2_indirect_inline_data_to_text(struct printbuf *out, void bch2_indirect_inline_data_to_text(struct printbuf *out,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#ifndef _BCACHEFS_REFLINK_H #ifndef _BCACHEFS_REFLINK_H
#define _BCACHEFS_REFLINK_H #define _BCACHEFS_REFLINK_H
const char *bch2_reflink_p_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_reflink_p_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_reflink_p_to_text(struct printbuf *, struct bch_fs *, void bch2_reflink_p_to_text(struct printbuf *, struct bch_fs *,
struct bkey_s_c); struct bkey_s_c);
bool bch2_reflink_p_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c); bool bch2_reflink_p_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
...@@ -15,7 +15,7 @@ bool bch2_reflink_p_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c); ...@@ -15,7 +15,7 @@ bool bch2_reflink_p_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
.atomic_trigger = bch2_mark_reflink_p, \ .atomic_trigger = bch2_mark_reflink_p, \
} }
const char *bch2_reflink_v_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_reflink_v_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_reflink_v_to_text(struct printbuf *, struct bch_fs *, void bch2_reflink_v_to_text(struct printbuf *, struct bch_fs *,
struct bkey_s_c); struct bkey_s_c);
int bch2_trans_mark_reflink_v(struct btree_trans *, struct bkey_s_c, int bch2_trans_mark_reflink_v(struct btree_trans *, struct bkey_s_c,
...@@ -29,8 +29,8 @@ int bch2_trans_mark_reflink_v(struct btree_trans *, struct bkey_s_c, ...@@ -29,8 +29,8 @@ int bch2_trans_mark_reflink_v(struct btree_trans *, struct bkey_s_c,
.atomic_trigger = bch2_mark_extent, \ .atomic_trigger = bch2_mark_extent, \
} }
const char *bch2_indirect_inline_data_invalid(const struct bch_fs *, int bch2_indirect_inline_data_invalid(const struct bch_fs *, struct bkey_s_c,
struct bkey_s_c); struct printbuf *);
void bch2_indirect_inline_data_to_text(struct printbuf *, void bch2_indirect_inline_data_to_text(struct printbuf *,
struct bch_fs *, struct bkey_s_c); struct bch_fs *, struct bkey_s_c);
int bch2_trans_mark_indirect_inline_data(struct btree_trans *, int bch2_trans_mark_indirect_inline_data(struct btree_trans *,
......
...@@ -26,39 +26,55 @@ void bch2_snapshot_to_text(struct printbuf *out, struct bch_fs *c, ...@@ -26,39 +26,55 @@ void bch2_snapshot_to_text(struct printbuf *out, struct bch_fs *c,
le32_to_cpu(s.v->subvol)); le32_to_cpu(s.v->subvol));
} }
const char *bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
struct bkey_s_c_snapshot s; struct bkey_s_c_snapshot s;
u32 i, id; u32 i, id;
if (bkey_cmp(k.k->p, POS(0, U32_MAX)) > 0 || if (bkey_cmp(k.k->p, POS(0, U32_MAX)) > 0 ||
bkey_cmp(k.k->p, POS(0, 1)) < 0) bkey_cmp(k.k->p, POS(0, 1)) < 0) {
return "bad pos"; pr_buf(err, "bad pos");
return -EINVAL;
}
if (bkey_val_bytes(k.k) != sizeof(struct bch_snapshot)) if (bkey_val_bytes(k.k) != sizeof(struct bch_snapshot)) {
return "bad val size"; pr_buf(err, "bad val size (%zu != %zu)",
bkey_val_bytes(k.k), sizeof(struct bch_snapshot));
return -EINVAL;
}
s = bkey_s_c_to_snapshot(k); s = bkey_s_c_to_snapshot(k);
id = le32_to_cpu(s.v->parent); id = le32_to_cpu(s.v->parent);
if (id && id <= k.k->p.offset) if (id && id <= k.k->p.offset) {
return "bad parent node"; pr_buf(err, "bad parent node (%u <= %llu)",
id, k.k->p.offset);
return -EINVAL;
}
if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1])) if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1])) {
return "children not normalized"; pr_buf(err, "children not normalized");
return -EINVAL;
}
if (s.v->children[0] && if (s.v->children[0] &&
s.v->children[0] == s.v->children[1]) s.v->children[0] == s.v->children[1]) {
return "duplicate child nodes"; pr_buf(err, "duplicate child nodes");
return -EINVAL;
}
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
id = le32_to_cpu(s.v->children[i]); id = le32_to_cpu(s.v->children[i]);
if (id >= k.k->p.offset) if (id >= k.k->p.offset) {
return "bad child node"; pr_buf(err, "bad child node (%u >= %llu)",
id, k.k->p.offset);
return -EINVAL;
}
} }
return NULL; return 0;
} }
int bch2_mark_snapshot(struct btree_trans *trans, int bch2_mark_snapshot(struct btree_trans *trans,
...@@ -729,18 +745,22 @@ static int bch2_delete_dead_snapshots_hook(struct btree_trans *trans, ...@@ -729,18 +745,22 @@ static int bch2_delete_dead_snapshots_hook(struct btree_trans *trans,
/* Subvolumes: */ /* Subvolumes: */
const char *bch2_subvolume_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_subvolume_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
if (bkey_cmp(k.k->p, SUBVOL_POS_MIN) < 0) if (bkey_cmp(k.k->p, SUBVOL_POS_MIN) < 0 ||
return "invalid pos"; bkey_cmp(k.k->p, SUBVOL_POS_MAX) > 0) {
pr_buf(err, "invalid pos");
if (bkey_cmp(k.k->p, SUBVOL_POS_MAX) > 0) return -EINVAL;
return "invalid pos"; }
if (bkey_val_bytes(k.k) != sizeof(struct bch_subvolume)) if (bkey_val_bytes(k.k) != sizeof(struct bch_subvolume)) {
return "bad val size"; pr_buf(err, "incorrect value size (%zu != %zu)",
bkey_val_bytes(k.k), sizeof(struct bch_subvolume));
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c, void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include "subvolume_types.h" #include "subvolume_types.h"
void bch2_snapshot_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_snapshot_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
const char *bch2_snapshot_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_snapshot_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
#define bch2_bkey_ops_snapshot (struct bkey_ops) { \ #define bch2_bkey_ops_snapshot (struct bkey_ops) { \
.key_invalid = bch2_snapshot_invalid, \ .key_invalid = bch2_snapshot_invalid, \
...@@ -96,7 +96,7 @@ int bch2_fs_snapshots_check(struct bch_fs *); ...@@ -96,7 +96,7 @@ int bch2_fs_snapshots_check(struct bch_fs *);
void bch2_fs_snapshots_exit(struct bch_fs *); void bch2_fs_snapshots_exit(struct bch_fs *);
int bch2_fs_snapshots_start(struct bch_fs *); int bch2_fs_snapshots_start(struct bch_fs *);
const char *bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_subvolume (struct bkey_ops) { \ #define bch2_bkey_ops_subvolume (struct bkey_ops) { \
......
...@@ -69,32 +69,51 @@ const struct bch_hash_desc bch2_xattr_hash_desc = { ...@@ -69,32 +69,51 @@ const struct bch_hash_desc bch2_xattr_hash_desc = {
.cmp_bkey = xattr_cmp_bkey, .cmp_bkey = xattr_cmp_bkey,
}; };
const char *bch2_xattr_invalid(const struct bch_fs *c, struct bkey_s_c k) int bch2_xattr_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err)
{ {
const struct xattr_handler *handler; const struct xattr_handler *handler;
struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k); struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k);
if (bkey_val_bytes(k.k) < sizeof(struct bch_xattr)) if (bkey_val_bytes(k.k) < sizeof(struct bch_xattr)) {
return "value too small"; pr_buf(err, "incorrect value size (%zu < %zu)",
bkey_val_bytes(k.k), sizeof(*xattr.v));
return -EINVAL;
}
if (bkey_val_u64s(k.k) < if (bkey_val_u64s(k.k) <
xattr_val_u64s(xattr.v->x_name_len, xattr_val_u64s(xattr.v->x_name_len,
le16_to_cpu(xattr.v->x_val_len))) le16_to_cpu(xattr.v->x_val_len))) {
return "value too small"; pr_buf(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 -EINVAL;
}
/* XXX why +4 ? */
if (bkey_val_u64s(k.k) > if (bkey_val_u64s(k.k) >
xattr_val_u64s(xattr.v->x_name_len, xattr_val_u64s(xattr.v->x_name_len,
le16_to_cpu(xattr.v->x_val_len) + 4)) le16_to_cpu(xattr.v->x_val_len) + 4)) {
return "value too big"; pr_buf(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 -EINVAL;
}
handler = bch2_xattr_type_to_handler(xattr.v->x_type); handler = bch2_xattr_type_to_handler(xattr.v->x_type);
if (!handler) if (!handler) {
return "invalid type"; pr_buf(err, "invalid type (%u)", xattr.v->x_type);
return -EINVAL;
}
if (memchr(xattr.v->x_name, '\0', xattr.v->x_name_len)) if (memchr(xattr.v->x_name, '\0', xattr.v->x_name_len)) {
return "xattr name has invalid characters"; pr_buf(err, "xattr name has invalid characters");
return -EINVAL;
}
return NULL; return 0;
} }
void bch2_xattr_to_text(struct printbuf *out, struct bch_fs *c, void bch2_xattr_to_text(struct printbuf *out, struct bch_fs *c,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
extern const struct bch_hash_desc bch2_xattr_hash_desc; extern const struct bch_hash_desc bch2_xattr_hash_desc;
const char *bch2_xattr_invalid(const struct bch_fs *, struct bkey_s_c); int bch2_xattr_invalid(const struct bch_fs *, struct bkey_s_c, struct printbuf *);
void bch2_xattr_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_xattr_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_xattr (struct bkey_ops) { \ #define bch2_bkey_ops_xattr (struct bkey_ops) { \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment