]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libblkid/src/superblocks/bcache.c
libblkid: (bcache) report version
[thirdparty/util-linux.git] / libblkid / src / superblocks / bcache.c
1 /*
2 * Copyright (C) 2013 Rolf Fokkens <rolf@fokkens.nl>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 *
7 * Based on code fragments from bcache-tools by Kent Overstreet:
8 * http://evilpiepirate.org/git/bcache-tools.git
9 */
10
11 #include <stddef.h>
12 #include <stdio.h>
13 #include <inttypes.h>
14
15 #include "superblocks.h"
16 #include "crc32c.h"
17 #include "crc64.h"
18 #include "xxhash.h"
19
20 #define SB_LABEL_SIZE 32
21 #define SB_JOURNAL_BUCKETS 256U
22
23 /*
24 * The bcache_super_block is adapted from struct cache_sb in kernel.
25 * https://github.com/torvalds/linux/blob/master/drivers/md/bcache/bcache_ondisk.h
26 */
27 struct bcache_super_block {
28 uint64_t csum;
29 uint64_t offset; /* where this super block was written */
30 uint64_t version;
31 uint8_t magic[16]; /* bcache file system identifier */
32 uint8_t uuid[16]; /* device identifier */
33 uint8_t set_info[16]; /* magic or uuid */
34 uint8_t label[SB_LABEL_SIZE];
35 uint64_t flags;
36 uint64_t seq;
37
38 uint64_t feature_compat;
39 uint64_t feature_incompat;
40 uint64_t feature_ro_compat;
41
42 uint64_t pad[5];
43
44 union {
45 struct {
46 /* Cache devices */
47 uint64_t nbuckets; /* device size */
48
49 uint16_t block_size; /* sectors */
50 uint16_t bucket_size; /* sectors */
51
52 uint16_t nr_in_set;
53 uint16_t nr_this_dev;
54 };
55 struct {
56 /* Backing devices */
57 uint64_t data_offset;
58 };
59 };
60
61 uint32_t last_mount;
62
63 uint16_t first_bucket;
64 union {
65 uint16_t njournal_buckets;
66 uint16_t keys;
67 };
68 uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */
69 uint16_t obso_bucket_size_hi; /* obsoleted */
70 } __attribute__((packed));
71
72 struct bcachefs_sb_field {
73 uint32_t u64s;
74 uint32_t type;
75 } __attribute__((packed));
76
77 struct bcachefs_sb_member {
78 uint8_t uuid[16];
79 uint64_t nbuckets;
80 uint16_t first_bucket;
81 uint16_t bucket_size;
82 uint32_t pad;
83 uint64_t last_mount;
84 uint64_t flags[2];
85 } __attribute__((packed));
86
87 struct bcachefs_sb_field_members {
88 struct bcachefs_sb_field field;
89 struct bcachefs_sb_member members[];
90 } __attribute__((packed));
91
92 enum bcachefs_sb_csum_type {
93 BCACHEFS_SB_CSUM_TYPE_NONE = 0,
94 BCACHEFS_SB_CSUM_TYPE_CRC32C = 1,
95 BCACHEFS_SB_CSUM_TYPE_CRC64 = 2,
96 BCACHEFS_SB_CSUM_TYPE_XXHASH = 7,
97 };
98
99 union bcachefs_sb_csum {
100 uint32_t crc32c;
101 uint64_t crc64;
102 XXH64_hash_t xxh64;
103 uint8_t raw[16];
104 } __attribute__((packed));
105
106 struct bcachefs_super_block {
107 union bcachefs_sb_csum csum;
108 uint16_t version;
109 uint16_t version_min;
110 uint16_t pad[2];
111 uint8_t magic[16];
112 uint8_t uuid[16];
113 uint8_t user_uuid[16];
114 uint8_t label[SB_LABEL_SIZE];
115 uint64_t offset;
116 uint64_t seq;
117 uint16_t block_size;
118 uint8_t dev_idx;
119 uint8_t nr_devices;
120 uint32_t u64s;
121 uint64_t time_base_lo;
122 uint32_t time_base_hi;
123 uint32_t time_precision;
124 uint64_t flags[8];
125 uint64_t features[2];
126 uint64_t compat[2];
127 uint8_t layout[512];
128 struct bcachefs_sb_field _start[];
129 } __attribute__((packed));
130
131 /* magic string */
132 #define BCACHE_SB_MAGIC "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81"
133 #define BCACHEFS_SB_MAGIC "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef"
134 /* magic string len */
135 #define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1)
136 /* super block offset */
137 #define BCACHE_SB_OFF 0x1000
138 /* supper block offset in kB */
139 #define BCACHE_SB_KBOFF (BCACHE_SB_OFF >> 10)
140 /* magic string offset within super block */
141 #define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic)
142 /* start of checksummed data within superblock */
143 #define BCACHE_SB_CSUMMED_START 8
144 /* granularity of offset and length fields within superblock */
145 #define BCACHEFS_SECTOR_SIZE 512
146 /* maximum superblock size */
147 #define BCACHEFS_SB_MAX_SIZE 4096
148 /* fields offset within super block */
149 #define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start)
150 /* tag value for members field */
151 #define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1
152
153 #define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8))
154
155 static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
156 const struct bcache_super_block *bcs)
157 {
158 const unsigned char *csummed;
159 size_t csummed_size;
160 uint64_t csum;
161
162 if (le16_to_cpu(bcs->keys) > ARRAY_SIZE(bcs->d))
163 return 0;
164
165 /* up to the end of bcs->d[] */
166 csummed_size = offsetof(typeof(*bcs), d) +
167 sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys);
168 csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
169 csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START,
170 csummed_size - BCACHE_SB_CSUMMED_START);
171 return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum));
172 }
173
174 static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
175 {
176 struct bcache_super_block *bcs;
177
178 bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block);
179 if (!bcs)
180 return errno ? -errno : BLKID_PROBE_NONE;
181
182 if (!bcache_verify_checksum(pr, mag, bcs))
183 return BLKID_PROBE_NONE;
184
185 if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
186 return BLKID_PROBE_NONE;
187
188 if (blkid_probe_sprintf_version(pr, "%"PRIu64, le64_to_cpu(bcs->version)) < 0)
189 return BLKID_PROBE_NONE;
190
191 if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
192 return BLKID_PROBE_NONE;
193
194 if (blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)) < 0)
195 return BLKID_PROBE_NONE;
196
197 blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
198
199 return BLKID_PROBE_OK;
200 }
201
202 static void probe_bcachefs_sb_members(blkid_probe pr,
203 const struct bcachefs_super_block *bcs,
204 const struct bcachefs_sb_field *field,
205 uint8_t dev_idx)
206 {
207 struct bcachefs_sb_field_members *members =
208 (struct bcachefs_sb_field_members *) field;
209 uint64_t sectors = 0;
210 uint8_t i;
211
212 if (BYTES(field) != offsetof(typeof(*members), members[bcs->nr_devices]))
213 return;
214
215 blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB");
216
217 for (i = 0; i < bcs->nr_devices; i++) {
218 struct bcachefs_sb_member *member = &members->members[i];
219 sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size);
220 }
221 blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE);
222 }
223
224 static int is_within_range(const void *start, uint64_t size, const void *end)
225 {
226 ptrdiff_t diff;
227
228 if (start >= end)
229 return 0; // should not happen
230
231 diff = (unsigned char *) end - (unsigned char *) start;
232 return size <= (uint64_t) diff;
233 }
234
235 static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs,
236 const unsigned char *sb_start, const unsigned char *sb_end)
237 {
238 const unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
239
240 while (1) {
241 struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr;
242 uint64_t field_size;
243 uint32_t type;
244
245 if (!is_within_range(field, sizeof(*field), sb_end))
246 break;
247
248 field_size = BYTES(field);
249
250 if (field_size < sizeof(*field))
251 break;
252
253 if (!is_within_range(field, field_size, sb_end))
254 break;
255
256 type = le32_to_cpu(field->type);
257 if (!type)
258 break;
259
260 if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS)
261 probe_bcachefs_sb_members(pr, bcs, field, bcs->dev_idx);
262
263 field_addr += BYTES(field);
264 }
265 }
266
267 static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs,
268 const unsigned char *sb, const unsigned char *sb_end)
269 {
270 uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58;
271 const unsigned char *checksummed_data_start = sb + sizeof(bcs->csum);
272 size_t checksummed_data_size = sb_end - checksummed_data_start;
273
274 switch (checksum_type) {
275 case BCACHEFS_SB_CSUM_TYPE_NONE:
276 return 1;
277 case BCACHEFS_SB_CSUM_TYPE_CRC32C: {
278 uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL;
279 return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c));
280 }
281 case BCACHEFS_SB_CSUM_TYPE_CRC64: {
282 uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size);
283 return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64));
284 }
285 case BCACHEFS_SB_CSUM_TYPE_XXHASH: {
286 XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0);
287 return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64));
288 }
289 default:
290 DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type));
291 return 1;
292 }
293 }
294
295 static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag)
296 {
297 struct bcachefs_super_block *bcs;
298 const unsigned char *sb, *sb_end;
299 uint64_t sb_size, blocksize;
300
301 bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block);
302 if (!bcs)
303 return errno ? -errno : BLKID_PROBE_NONE;
304
305 if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / BCACHEFS_SECTOR_SIZE)
306 return BLKID_PROBE_NONE;
307
308 if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices)
309 return BLKID_PROBE_NONE;
310
311 sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs);
312 if (sb_size > BCACHEFS_SB_MAX_SIZE)
313 return BLKID_PROBE_NONE;
314
315 sb = blkid_probe_get_sb_buffer(pr, mag, sb_size);
316 if (!sb)
317 return BLKID_PROBE_NONE;
318 sb_end = sb + sb_size;
319
320 if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end))
321 return BLKID_PROBE_NONE;
322
323 blkid_probe_set_uuid(pr, bcs->user_uuid);
324 blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label));
325 blkid_probe_sprintf_version(pr, "%d", le16_to_cpu(bcs->version));
326 blocksize = le16_to_cpu(bcs->block_size);
327 blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE);
328 blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE);
329 blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
330
331 probe_bcachefs_sb_fields(pr, bcs, sb, sb_end);
332
333 return BLKID_PROBE_OK;
334 }
335
336 const struct blkid_idinfo bcache_idinfo =
337 {
338 .name = "bcache",
339 .usage = BLKID_USAGE_OTHER,
340 .probefunc = probe_bcache,
341 .minsz = 8192,
342 .magics =
343 {
344 {
345 .magic = BCACHE_SB_MAGIC,
346 .len = BCACHE_SB_MAGIC_LEN,
347 .kboff = BCACHE_SB_KBOFF,
348 .sboff = BCACHE_SB_MAGIC_OFF
349 },
350 { NULL }
351 }
352 };
353
354 const struct blkid_idinfo bcachefs_idinfo =
355 {
356 .name = "bcachefs",
357 .usage = BLKID_USAGE_FILESYSTEM,
358 .probefunc = probe_bcachefs,
359 .minsz = 256 * BCACHEFS_SECTOR_SIZE,
360 .magics = {
361 {
362 .magic = BCACHE_SB_MAGIC,
363 .len = BCACHE_SB_MAGIC_LEN,
364 .kboff = BCACHE_SB_KBOFF,
365 .sboff = BCACHE_SB_MAGIC_OFF,
366 },
367 {
368 .magic = BCACHEFS_SB_MAGIC,
369 .len = BCACHE_SB_MAGIC_LEN,
370 .kboff = BCACHE_SB_KBOFF,
371 .sboff = BCACHE_SB_MAGIC_OFF,
372 },
373 { NULL }
374 }
375 };