// SPDX-License-Identifier: GPL-2.0 #include "bcachefs.h" #include "bkey_methods.h" #include "btree_types.h" #include "alloc_background.h" #include "dirent.h" #include "error.h" #include "extents.h" #include "inode.h" #include "quota.h" #include "xattr.h" const struct bkey_ops bch2_bkey_ops[] = { [BKEY_TYPE_EXTENTS] = bch2_bkey_extent_ops, [BKEY_TYPE_INODES] = bch2_bkey_inode_ops, [BKEY_TYPE_DIRENTS] = bch2_bkey_dirent_ops, [BKEY_TYPE_XATTRS] = bch2_bkey_xattr_ops, [BKEY_TYPE_ALLOC] = bch2_bkey_alloc_ops, [BKEY_TYPE_QUOTAS] = bch2_bkey_quota_ops, [BKEY_TYPE_BTREE] = bch2_bkey_btree_ops, }; const char *bch2_bkey_val_invalid(struct bch_fs *c, enum bkey_type type, struct bkey_s_c k) { const struct bkey_ops *ops = &bch2_bkey_ops[type]; switch (k.k->type) { case KEY_TYPE_DELETED: case KEY_TYPE_DISCARD: return NULL; case KEY_TYPE_ERROR: return bkey_val_bytes(k.k) != 0 ? "value size should be zero" : NULL; case KEY_TYPE_COOKIE: return bkey_val_bytes(k.k) != sizeof(struct bch_cookie) ? "incorrect value size" : NULL; default: if (k.k->type < KEY_TYPE_GENERIC_NR) return "invalid type"; return ops->key_invalid(c, k); } } const char *__bch2_bkey_invalid(struct bch_fs *c, enum bkey_type type, struct bkey_s_c k) { const struct bkey_ops *ops = &bch2_bkey_ops[type]; if (k.k->u64s < BKEY_U64s) return "u64s too small"; if (!ops->is_extents) { if (k.k->size) return "nonzero size field"; } else { if ((k.k->size == 0) != bkey_deleted(k.k)) return "bad size field"; } if (ops->is_extents && !k.k->size && !bkey_deleted(k.k)) return "zero size field"; if (k.k->p.snapshot) return "nonzero snapshot"; if (type != BKEY_TYPE_BTREE && !bkey_cmp(k.k->p, POS_MAX)) return "POS_MAX key"; return NULL; } const char *bch2_bkey_invalid(struct bch_fs *c, enum bkey_type type, struct bkey_s_c k) { return __bch2_bkey_invalid(c, type, k) ?: bch2_bkey_val_invalid(c, type, k); } const char *bch2_bkey_in_btree_node(struct btree *b, struct bkey_s_c k) { if (bkey_cmp(bkey_start_pos(k.k), b->data->min_key) < 0) return "key before start of btree node"; if (bkey_cmp(k.k->p, b->data->max_key) > 0) return "key past end of btree node"; return NULL; } void bch2_bkey_debugcheck(struct bch_fs *c, struct btree *b, struct bkey_s_c k) { enum bkey_type type = btree_node_type(b); const struct bkey_ops *ops = &bch2_bkey_ops[type]; const char *invalid; BUG_ON(!k.k->u64s); invalid = bch2_bkey_invalid(c, type, k) ?: bch2_bkey_in_btree_node(b, k); if (invalid) { char buf[160]; bch2_bkey_val_to_text(&PBUF(buf), c, type, k); bch2_fs_bug(c, "invalid bkey %s: %s", buf, invalid); return; } if (k.k->type >= KEY_TYPE_GENERIC_NR && ops->key_debugcheck) ops->key_debugcheck(c, b, k); } void bch2_bpos_to_text(struct printbuf *out, struct bpos pos) { if (!bkey_cmp(pos, POS_MIN)) pr_buf(out, "POS_MIN"); else if (!bkey_cmp(pos, POS_MAX)) pr_buf(out, "POS_MAX"); else pr_buf(out, "%llu:%llu", pos.inode, pos.offset); } void bch2_bkey_to_text(struct printbuf *out, const struct bkey *k) { pr_buf(out, "u64s %u type %u ", k->u64s, k->type); bch2_bpos_to_text(out, k->p); pr_buf(out, " snap %u len %u ver %llu", k->p.snapshot, k->size, k->version.lo); } void bch2_val_to_text(struct printbuf *out, struct bch_fs *c, enum bkey_type type, struct bkey_s_c k) { const struct bkey_ops *ops = &bch2_bkey_ops[type]; switch (k.k->type) { case KEY_TYPE_DELETED: pr_buf(out, " deleted"); break; case KEY_TYPE_DISCARD: pr_buf(out, " discard"); break; case KEY_TYPE_ERROR: pr_buf(out, " error"); break; case KEY_TYPE_COOKIE: pr_buf(out, " cookie"); break; default: if (k.k->type >= KEY_TYPE_GENERIC_NR && ops->val_to_text) ops->val_to_text(out, c, k); break; } } void bch2_bkey_val_to_text(struct printbuf *out, struct bch_fs *c, enum bkey_type type, struct bkey_s_c k) { bch2_bkey_to_text(out, k.k); pr_buf(out, ": "); bch2_val_to_text(out, c, type, k); } void bch2_bkey_swab(enum bkey_type type, const struct bkey_format *f, struct bkey_packed *k) { const struct bkey_ops *ops = &bch2_bkey_ops[type]; bch2_bkey_swab_key(f, k); if (ops->swab) ops->swab(f, k); }