]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libblkid/src/superblocks/bcache.c
libblkid: (bcachefs) add support for superblock at end of disk
[thirdparty/util-linux.git] / libblkid / src / superblocks / bcache.c
CommitLineData
a083b725
RF
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>
1b2efad2 13#include <inttypes.h>
a083b725
RF
14
15#include "superblocks.h"
e9e51878 16#include "crc32c.h"
93fc0656 17#include "crc64.h"
f5c84384 18#include "xxhash.h"
a083b725
RF
19
20#define SB_LABEL_SIZE 32
d2f4b5c8 21#define SB_JOURNAL_BUCKETS 256U
a083b725 22
ebd13d3f 23/*
d2f4b5c8
TW
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
ebd13d3f 26 */
a083b725
RF
27struct bcache_super_block {
28 uint64_t csum;
ebd13d3f 29 uint64_t offset; /* where this super block was written */
a083b725 30 uint64_t version;
ebd13d3f
SK
31 uint8_t magic[16]; /* bcache file system identifier */
32 uint8_t uuid[16]; /* device identifier */
d2f4b5c8
TW
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 */
b02fc968 70} __attribute__((packed));
a083b725 71
4ad02a25
TW
72struct bcachefs_sb_field {
73 uint32_t u64s;
74 uint32_t type;
75} __attribute__((packed));
76
77struct bcachefs_sb_member {
ba5c3e73
TW
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));
4ad02a25
TW
86
87struct bcachefs_sb_field_members {
88 struct bcachefs_sb_field field;
89 struct bcachefs_sb_member members[];
90} __attribute__((packed));
91
da91337a
TW
92struct bcachefs_sb_disk_group {
93 uint8_t label[SB_LABEL_SIZE];
94 uint64_t flags[2];
95} __attribute__((packed));
96
97struct bcachefs_sb_field_disk_groups {
98 struct bcachefs_sb_field field;
99 struct bcachefs_sb_disk_group disk_groups[];
100} __attribute__((packed));
101
e9e51878
TW
102enum bcachefs_sb_csum_type {
103 BCACHEFS_SB_CSUM_TYPE_NONE = 0,
104 BCACHEFS_SB_CSUM_TYPE_CRC32C = 1,
4a57b0b4 105 BCACHEFS_SB_CSUM_TYPE_CRC64 = 2,
f5c84384 106 BCACHEFS_SB_CSUM_TYPE_XXHASH = 7,
e9e51878
TW
107};
108
109union bcachefs_sb_csum {
110 uint32_t crc32c;
4a57b0b4 111 uint64_t crc64;
f5c84384 112 XXH64_hash_t xxh64;
e9e51878
TW
113 uint8_t raw[16];
114} __attribute__((packed));
115
48d57379
CG
116struct bcachefs_sb_layout {
117 uint8_t magic[16];
118 uint8_t layout_type;
119 uint8_t sb_max_size_bits;
120 uint8_t nr_superblocks;
121 uint8_t pad[5];
122 uint64_t sb_offset[61];
123} __attribute__((packed));
124
64094168 125struct bcachefs_super_block {
e9e51878 126 union bcachefs_sb_csum csum;
64094168
TW
127 uint16_t version;
128 uint16_t version_min;
129 uint16_t pad[2];
130 uint8_t magic[16];
131 uint8_t uuid[16];
132 uint8_t user_uuid[16];
133 uint8_t label[SB_LABEL_SIZE];
134 uint64_t offset;
135 uint64_t seq;
136 uint16_t block_size;
4ad02a25
TW
137 uint8_t dev_idx;
138 uint8_t nr_devices;
139 uint32_t u64s;
140 uint64_t time_base_lo;
141 uint32_t time_base_hi;
142 uint32_t time_precision;
143 uint64_t flags[8];
144 uint64_t features[2];
145 uint64_t compat[2];
48d57379 146 struct bcachefs_sb_layout layout;
4ad02a25 147 struct bcachefs_sb_field _start[];
64094168
TW
148} __attribute__((packed));
149
a083b725 150/* magic string */
10211619 151#define BCACHE_SB_MAGIC "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81"
baf87230 152#define BCACHEFS_SB_MAGIC "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef"
a083b725 153/* magic string len */
10211619 154#define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1)
a083b725 155/* super block offset */
befe455f 156#define BCACHE_SB_OFF 0x1000U
a083b725
RF
157/* supper block offset in kB */
158#define BCACHE_SB_KBOFF (BCACHE_SB_OFF >> 10)
159/* magic string offset within super block */
77da12e7 160#define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic)
93fc0656 161/* start of checksummed data within superblock */
befe455f 162#define BCACHE_SB_CSUMMED_START 8U
64094168 163/* granularity of offset and length fields within superblock */
befe455f
TW
164#define BCACHEFS_SECTOR_SIZE 512U
165/* maximum superblock size shift */
166#define BCACHEFS_SB_MAX_SIZE_SHIFT 0x10U
eef4dbe0 167/* maximum superblock size */
befe455f 168#define BCACHEFS_SB_MAX_SIZE (1U << BCACHEFS_SB_MAX_SIZE_SHIFT)
4ad02a25 169/* fields offset within super block */
77da12e7 170#define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start)
4ad02a25
TW
171/* tag value for members field */
172#define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1
da91337a
TW
173/* tag value for disk_groups field */
174#define BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS 5
8beea162
TW
175/* version splitting helpers */
176#define BCH_VERSION_MAJOR(_v) ((uint16_t) ((_v) >> 10))
177#define BCH_VERSION_MINOR(_v) ((uint16_t) ((_v) & ~(~0U << 10)))
4ad02a25 178
df6e200f 179#define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8))
93fc0656
TW
180
181static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
182 const struct bcache_super_block *bcs)
183{
71406358
TW
184 const unsigned char *csummed;
185 size_t csummed_size;
186 uint64_t csum;
187
188 if (le16_to_cpu(bcs->keys) > ARRAY_SIZE(bcs->d))
189 return 0;
190
191 /* up to the end of bcs->d[] */
c81f7efc 192 csummed_size = offsetof(__typeof__(*bcs), d) +
71406358
TW
193 sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys);
194 csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
195 csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START,
196 csummed_size - BCACHE_SB_CSUMMED_START);
93fc0656
TW
197 return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum));
198}
a083b725 199
a083b725
RF
200static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
201{
202 struct bcache_super_block *bcs;
203
204 bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block);
205 if (!bcs)
37f40602 206 return errno ? -errno : BLKID_PROBE_NONE;
a083b725 207
93fc0656
TW
208 if (!bcache_verify_checksum(pr, mag, bcs))
209 return BLKID_PROBE_NONE;
210
a083b725 211 if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
37f40602 212 return BLKID_PROBE_NONE;
a083b725 213
1b2efad2
TW
214 if (blkid_probe_sprintf_version(pr, "%"PRIu64, le64_to_cpu(bcs->version)) < 0)
215 return BLKID_PROBE_NONE;
216
a083b725 217 if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
37f40602 218 return BLKID_PROBE_NONE;
a083b725 219
11d0724f
TW
220 if (blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)) < 0)
221 return BLKID_PROBE_NONE;
222
9bd80748
TW
223 if (blkid_probe_set_block_size(pr, le16_to_cpu(bcs->block_size) * 512))
224 return BLKID_PROBE_NONE;
225
1db1994e
TW
226 blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
227
37f40602 228 return BLKID_PROBE_OK;
cf922860 229}
a083b725 230
ba5c3e73
TW
231static void probe_bcachefs_sb_members(blkid_probe pr,
232 const struct bcachefs_super_block *bcs,
233 const struct bcachefs_sb_field *field,
e601773d 234 uint8_t dev_idx)
4ad02a25 235{
77da12e7
KZ
236 struct bcachefs_sb_field_members *members =
237 (struct bcachefs_sb_field_members *) field;
238 uint64_t sectors = 0;
239 uint8_t i;
4ad02a25 240
c81f7efc 241 if (BYTES(field) != offsetof(__typeof__(*members), members[bcs->nr_devices]))
4ad02a25
TW
242 return;
243
d9c72ffc
TW
244 blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB");
245
77da12e7 246 for (i = 0; i < bcs->nr_devices; i++) {
ba5c3e73
TW
247 struct bcachefs_sb_member *member = &members->members[i];
248 sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size);
249 }
250 blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE);
4ad02a25
TW
251}
252
da91337a
TW
253static void probe_bcachefs_sb_disk_groups(blkid_probe pr,
254 const struct bcachefs_super_block *bcs,
255 const struct bcachefs_sb_field *field,
256 uint8_t dev_idx)
257{
258 struct bcachefs_sb_field_disk_groups *disk_groups =
259 (struct bcachefs_sb_field_disk_groups *) field;
260
c81f7efc 261 if (BYTES(field) != offsetof(__typeof__(*disk_groups), disk_groups[bcs->nr_devices]))
da91337a
TW
262 return;
263
264 blkid_probe_set_id_label(pr, "LABEL_SUB",
265 disk_groups->disk_groups[dev_idx].label,
266 sizeof(disk_groups->disk_groups[dev_idx].label));
267}
268
7eba8f98 269static int is_within_range(const void *start, uint64_t size, const void *end)
46ce48b6
TW
270{
271 ptrdiff_t diff;
272
273 if (start >= end)
274 return 0; // should not happen
275
276 diff = (unsigned char *) end - (unsigned char *) start;
277 return size <= (uint64_t) diff;
278}
279
e9e51878 280static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs,
7eba8f98 281 const unsigned char *sb_start, const unsigned char *sb_end)
4ad02a25 282{
7eba8f98 283 const unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
4ad02a25
TW
284
285 while (1) {
286 struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr;
64bfbdb4 287 uint64_t field_size;
dcf4b1b2 288 uint32_t type;
4ad02a25 289
46ce48b6 290 if (!is_within_range(field, sizeof(*field), sb_end))
64bfbdb4
TW
291 break;
292
293 field_size = BYTES(field);
294
295 if (field_size < sizeof(*field))
296 break;
297
46ce48b6 298 if (!is_within_range(field, field_size, sb_end))
64bfbdb4 299 break;
4ad02a25 300
77da12e7 301 type = le32_to_cpu(field->type);
4ad02a25
TW
302 if (!type)
303 break;
304
305 if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS)
e601773d 306 probe_bcachefs_sb_members(pr, bcs, field, bcs->dev_idx);
4ad02a25 307
da91337a
TW
308 if (type == BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS)
309 probe_bcachefs_sb_disk_groups(pr, bcs, field, bcs->dev_idx);
310
4ad02a25
TW
311 field_addr += BYTES(field);
312 }
313}
314
e9e51878 315static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs,
7eba8f98 316 const unsigned char *sb, const unsigned char *sb_end)
e9e51878
TW
317{
318 uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58;
7eba8f98 319 const unsigned char *checksummed_data_start = sb + sizeof(bcs->csum);
e9e51878 320 size_t checksummed_data_size = sb_end - checksummed_data_start;
77da12e7 321
e9e51878
TW
322 switch (checksum_type) {
323 case BCACHEFS_SB_CSUM_TYPE_NONE:
324 return 1;
325 case BCACHEFS_SB_CSUM_TYPE_CRC32C: {
326 uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL;
327 return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c));
328 }
4a57b0b4
TW
329 case BCACHEFS_SB_CSUM_TYPE_CRC64: {
330 uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size);
331 return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64));
332 }
f5c84384
TW
333 case BCACHEFS_SB_CSUM_TYPE_XXHASH: {
334 XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0);
335 return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64));
336 }
e9e51878
TW
337 default:
338 DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type));
339 return 1;
340 }
341}
342
64094168
TW
343static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag)
344{
345 struct bcachefs_super_block *bcs;
7eba8f98 346 const unsigned char *sb, *sb_end;
ca466a27 347 uint64_t sb_size, blocksize, offset_sectors;
8beea162 348 uint16_t version;
64094168
TW
349
350 bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block);
351 if (!bcs)
352 return errno ? -errno : BLKID_PROBE_NONE;
353
ca466a27
TW
354 offset_sectors = blkid_probe_get_idmag_off(pr, mag) / BCACHEFS_SECTOR_SIZE;
355 if (le64_to_cpu(bcs->offset) != offset_sectors)
64094168
TW
356 return BLKID_PROBE_NONE;
357
ba5c3e73
TW
358 if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices)
359 return BLKID_PROBE_NONE;
360
77da12e7 361 sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs);
48d57379 362
eef4dbe0
TW
363 if (sb_size > BCACHEFS_SB_MAX_SIZE)
364 return BLKID_PROBE_NONE;
365
befe455f
TW
366 if (bcs->layout.sb_max_size_bits > BCACHEFS_SB_MAX_SIZE_SHIFT)
367 return BLKID_PROBE_NONE;
368
369 if (sb_size > (BCACHEFS_SECTOR_SIZE << bcs->layout.sb_max_size_bits))
370 return BLKID_PROBE_NONE;
371
77da12e7
KZ
372 sb = blkid_probe_get_sb_buffer(pr, mag, sb_size);
373 if (!sb)
374 return BLKID_PROBE_NONE;
375 sb_end = sb + sb_size;
376
377 if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end))
378 return BLKID_PROBE_NONE;
379
64094168
TW
380 blkid_probe_set_uuid(pr, bcs->user_uuid);
381 blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label));
8beea162
TW
382 version = le16_to_cpu(bcs->version);
383 blkid_probe_sprintf_version(pr, "%"PRIu16".%"PRIu16,
384 BCH_VERSION_MAJOR(version),
385 BCH_VERSION_MINOR(version));
64094168
TW
386 blocksize = le16_to_cpu(bcs->block_size);
387 blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE);
ba5c3e73 388 blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE);
64094168 389 blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
77da12e7
KZ
390
391 probe_bcachefs_sb_fields(pr, bcs, sb, sb_end);
392
393 return BLKID_PROBE_OK;
64094168
TW
394}
395
a083b725
RF
396const struct blkid_idinfo bcache_idinfo =
397{
398 .name = "bcache",
399 .usage = BLKID_USAGE_OTHER,
400 .probefunc = probe_bcache,
401 .minsz = 8192,
402 .magics =
403 {
10211619
KZ
404 {
405 .magic = BCACHE_SB_MAGIC,
406 .len = BCACHE_SB_MAGIC_LEN,
407 .kboff = BCACHE_SB_KBOFF,
408 .sboff = BCACHE_SB_MAGIC_OFF
409 },
a083b725
RF
410 { NULL }
411 }
412};
413
64094168
TW
414const struct blkid_idinfo bcachefs_idinfo =
415{
416 .name = "bcachefs",
417 .usage = BLKID_USAGE_FILESYSTEM,
418 .probefunc = probe_bcachefs,
419 .minsz = 256 * BCACHEFS_SECTOR_SIZE,
420 .magics = {
421 {
422 .magic = BCACHE_SB_MAGIC,
423 .len = BCACHE_SB_MAGIC_LEN,
424 .kboff = BCACHE_SB_KBOFF,
425 .sboff = BCACHE_SB_MAGIC_OFF,
426 },
427 {
428 .magic = BCACHEFS_SB_MAGIC,
429 .len = BCACHE_SB_MAGIC_LEN,
430 .kboff = BCACHE_SB_KBOFF,
431 .sboff = BCACHE_SB_MAGIC_OFF,
432 },
8ca20ff6
TW
433 {
434 .magic = BCACHEFS_SB_MAGIC,
435 .len = BCACHE_SB_MAGIC_LEN,
436 .kboff = 1 << 11,
437 .sboff = BCACHE_SB_MAGIC_OFF,
438 },
cd6812cb
TW
439 {
440 .magic = BCACHEFS_SB_MAGIC,
441 .len = BCACHE_SB_MAGIC_LEN,
442 .kboff = -(1 << 10),
443 .sboff = BCACHE_SB_MAGIC_OFF,
444 },
64094168
TW
445 { NULL }
446 }
447};