summaryrefslogtreecommitdiffstats
path: root/fs/bcachefs/checksum.h
blob: 1b8c2c1016dc6347ce12ef3161d4723835dfa56e (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BCACHEFS_CHECKSUM_H
#define _BCACHEFS_CHECKSUM_H

#include "bcachefs.h"
#include "extents_types.h"
#include "super-io.h"

#include <linux/crc64.h>
#include <crypto/chacha.h>

static inline bool bch2_checksum_mergeable(unsigned type)
{

	switch (type) {
	case BCH_CSUM_none:
	case BCH_CSUM_crc32c:
	case BCH_CSUM_crc64:
		return true;
	default:
		return false;
	}
}

struct bch_csum bch2_checksum_merge(unsigned, struct bch_csum,
				    struct bch_csum, size_t);

#define BCH_NONCE_EXTENT	cpu_to_le32(1 << 28)
#define BCH_NONCE_BTREE		cpu_to_le32(2 << 28)
#define BCH_NONCE_JOURNAL	cpu_to_le32(3 << 28)
#define BCH_NONCE_PRIO		cpu_to_le32(4 << 28)
#define BCH_NONCE_POLY		cpu_to_le32(1 << 31)

struct bch_csum bch2_checksum(struct bch_fs *, unsigned, struct nonce,
			     const void *, size_t);

/*
 * This is used for various on disk data structures - bch_sb, prio_set, bset,
 * jset: The checksum is _always_ the first field of these structs
 */
#define csum_vstruct(_c, _type, _nonce, _i)				\
({									\
	const void *_start = ((const void *) (_i)) + sizeof((_i)->csum);\
									\
	bch2_checksum(_c, _type, _nonce, _start, vstruct_end(_i) - _start);\
})

static inline void bch2_csum_to_text(struct printbuf *out,
				     enum bch_csum_type type,
				     struct bch_csum csum)
{
	const u8 *p = (u8 *) &csum;
	unsigned bytes = type < BCH_CSUM_NR ? bch_crc_bytes[type] : 16;

	for (unsigned i = 0; i < bytes; i++)
		prt_hex_byte(out, p[i]);
}

static inline void bch2_csum_err_msg(struct printbuf *out,
				     enum bch_csum_type type,
				     struct bch_csum expected,
				     struct bch_csum got)
{
	prt_printf(out, "checksum error: got ");
	bch2_csum_to_text(out, type, got);
	prt_str(out, " should be ");
	bch2_csum_to_text(out, type, expected);
	prt_printf(out, " type %s", bch2_csum_types[type]);
}

int bch2_chacha_encrypt_key(struct bch_key *, struct nonce, void *, size_t);
int bch2_request_key(struct bch_sb *, struct bch_key *);
#ifndef __KERNEL__
int bch2_revoke_key(struct bch_sb *);
#endif

int bch2_encrypt(struct bch_fs *, unsigned, struct nonce,
		 void *data, size_t);

struct bch_csum bch2_checksum_bio(struct bch_fs *, unsigned,
				  struct nonce, struct bio *);

int bch2_rechecksum_bio(struct bch_fs *, struct bio *, struct bversion,
			struct bch_extent_crc_unpacked,
			struct bch_extent_crc_unpacked *,
			struct bch_extent_crc_unpacked *,
			unsigned, unsigned, unsigned);

int __bch2_encrypt_bio(struct bch_fs *, unsigned,
		       struct nonce, struct bio *);

static inline int bch2_encrypt_bio(struct bch_fs *c, unsigned type,
				   struct nonce nonce, struct bio *bio)
{
	return bch2_csum_type_is_encryption(type)
		? __bch2_encrypt_bio(c, type, nonce, bio)
		: 0;
}

extern const struct bch_sb_field_ops bch_sb_field_ops_crypt;

int bch2_decrypt_sb_key(struct bch_fs *, struct bch_sb_field_crypt *,
			struct bch_key *);

int bch2_disable_encryption(struct bch_fs *);
int bch2_enable_encryption(struct bch_fs *, bool);

void bch2_fs_encryption_exit(struct bch_fs *);
int bch2_fs_encryption_init(struct bch_fs *);

static inline enum bch_csum_type bch2_csum_opt_to_type(enum bch_csum_opts type,
						       bool data)
{
	switch (type) {
	case BCH_CSUM_OPT_none:
		return BCH_CSUM_none;
	case BCH_CSUM_OPT_crc32c:
		return data ? BCH_CSUM_crc32c : BCH_CSUM_crc32c_nonzero;
	case BCH_CSUM_OPT_crc64:
		return data ? BCH_CSUM_crc64 : BCH_CSUM_crc64_nonzero;
	case BCH_CSUM_OPT_xxhash:
		return BCH_CSUM_xxhash;
	default:
		BUG();
	}
}

static inline enum bch_csum_type bch2_data_checksum_type(struct bch_fs *c,
							 struct bch_io_opts opts)
{
	if (opts.nocow)
		return 0;

	if (c->sb.encryption_type)
		return c->opts.wide_macs
			? BCH_CSUM_chacha20_poly1305_128
			: BCH_CSUM_chacha20_poly1305_80;

	return bch2_csum_opt_to_type(opts.data_checksum, true);
}

static inline enum bch_csum_type bch2_meta_checksum_type(struct bch_fs *c)
{
	if (c->sb.encryption_type)
		return BCH_CSUM_chacha20_poly1305_128;

	return bch2_csum_opt_to_type(c->opts.metadata_checksum, false);
}

static inline bool bch2_checksum_type_valid(const struct bch_fs *c,
					   unsigned type)
{
	if (type >= BCH_CSUM_NR)
		return false;

	if (bch2_csum_type_is_encryption(type) && !c->chacha20)
		return false;

	return true;
}

/* returns true if not equal */
static inline bool bch2_crc_cmp(struct bch_csum l, struct bch_csum r)
{
	/*
	 * XXX: need some way of preventing the compiler from optimizing this
	 * into a form that isn't constant time..
	 */
	return ((l.lo ^ r.lo) | (l.hi ^ r.hi)) != 0;
}

/* for skipping ahead and encrypting/decrypting at an offset: */
static inline struct nonce nonce_add(struct nonce nonce, unsigned offset)
{
	EBUG_ON(offset & (CHACHA_BLOCK_SIZE - 1));

	le32_add_cpu(&nonce.d[0], offset / CHACHA_BLOCK_SIZE);
	return nonce;
}

static inline struct nonce null_nonce(void)
{
	struct nonce ret;

	memset(&ret, 0, sizeof(ret));
	return ret;
}

static inline struct nonce extent_nonce(struct bversion version,
					struct bch_extent_crc_unpacked crc)
{
	unsigned compression_type = crc_is_compressed(crc)
		? crc.compression_type
		: 0;
	unsigned size = compression_type ? crc.uncompressed_size : 0;
	struct nonce nonce = (struct nonce) {{
		[0] = cpu_to_le32(size << 22),
		[1] = cpu_to_le32(version.lo),
		[2] = cpu_to_le32(version.lo >> 32),
		[3] = cpu_to_le32(version.hi|
				  (compression_type << 24))^BCH_NONCE_EXTENT,
	}};

	return nonce_add(nonce, crc.nonce << 9);
}

static inline bool bch2_key_is_encrypted(struct bch_encrypted_key *key)
{
	return le64_to_cpu(key->magic) != BCH_KEY_MAGIC;
}

static inline struct nonce __bch2_sb_key_nonce(struct bch_sb *sb)
{
	__le64 magic = __bch2_sb_magic(sb);

	return (struct nonce) {{
		[0] = 0,
		[1] = 0,
		[2] = ((__le32 *) &magic)[0],
		[3] = ((__le32 *) &magic)[1],
	}};
}

static inline struct nonce bch2_sb_key_nonce(struct bch_fs *c)
{
	__le64 magic = bch2_sb_magic(c);

	return (struct nonce) {{
		[0] = 0,
		[1] = 0,
		[2] = ((__le32 *) &magic)[0],
		[3] = ((__le32 *) &magic)[1],
	}};
}

#endif /* _BCACHEFS_CHECKSUM_H */