summaryrefslogtreecommitdiff
path: root/libbcachefs/bkey_methods.c
blob: cd9a60c1144a7fed0ea610dcc5c89506ba72d043 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

#include "bcachefs.h"
#include "bkey_methods.h"
#include "btree_types.h"
#include "dirent.h"
#include "error.h"
#include "extents.h"
#include "inode.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_BTREE]	= &bch2_bkey_btree_ops,
};

/* Returns string indicating reason for being invalid, or NULL if valid: */
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 (k.k->size &&
	    (bkey_deleted(k.k) || !ops->is_extents))
		return "nonzero size field";

	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_btree_bkey_invalid(struct bch_fs *c, 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";

	if (k.k->p.snapshot)
		return "nonzero snapshot";

	return bch2_bkey_invalid(c, btree_node_type(b), k);
}

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_btree_bkey_invalid(c, b, k);
	if (invalid) {
		char buf[160];

		bch2_bkey_val_to_text(c, type, buf, sizeof(buf), 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);
}

char *bch2_val_to_text(struct bch_fs *c, enum bkey_type type,
		       char *buf, size_t size, struct bkey_s_c k)
{
	const struct bkey_ops *ops = bch2_bkey_ops[type];

	if (k.k->type >= KEY_TYPE_GENERIC_NR &&
	    ops->val_to_text)
		ops->val_to_text(c, buf, size, k);

	return buf;
}

char *bch2_bkey_val_to_text(struct bch_fs *c, enum bkey_type type,
			    char *buf, size_t size, struct bkey_s_c k)
{
	const struct bkey_ops *ops = bch2_bkey_ops[type];
	char *out = buf, *end = buf + size;

	out += bch2_bkey_to_text(out, end - out, k.k);

	if (k.k->type >= KEY_TYPE_GENERIC_NR &&
	    ops->val_to_text) {
		out += scnprintf(out, end - out, ": ");
		ops->val_to_text(c, out, end - out, k);
	}

	return buf;
}

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);
}