Coverage Report

Created: 2025-10-12 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/superblocks/bcache.c
Line
Count
Source
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
struct bcachefs_sb_disk_group {
93
  uint8_t   label[SB_LABEL_SIZE];
94
  uint64_t  flags[2];
95
} __attribute__((packed));
96
97
struct bcachefs_sb_field_disk_groups {
98
  struct bcachefs_sb_field  field;
99
  struct bcachefs_sb_disk_group   disk_groups[];
100
}  __attribute__((packed));
101
102
enum bcachefs_sb_csum_type {
103
  BCACHEFS_SB_CSUM_TYPE_NONE = 0,
104
  BCACHEFS_SB_CSUM_TYPE_CRC32C = 1,
105
  BCACHEFS_SB_CSUM_TYPE_CRC64 = 2,
106
  BCACHEFS_SB_CSUM_TYPE_XXHASH = 7,
107
};
108
109
union bcachefs_sb_csum {
110
  uint32_t crc32c;
111
  uint64_t crc64;
112
  XXH64_hash_t xxh64;
113
  uint8_t raw[16];
114
} __attribute__((packed));
115
116
struct 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
125
struct bcachefs_super_block {
126
  union bcachefs_sb_csum  csum;
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;
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];
146
  struct bcachefs_sb_layout layout;
147
  struct bcachefs_sb_field _start[];
148
}  __attribute__((packed));
149
150
/* magic string */
151
#define BCACHE_SB_MAGIC     "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81"
152
#define BCACHEFS_SB_MAGIC   "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef"
153
/* magic string len */
154
#define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1)
155
/* super block offset */
156
66
#define BCACHE_SB_OFF       0x1000U
157
/* supper block offset in kB */
158
#define BCACHE_SB_KBOFF     (BCACHE_SB_OFF >> 10)
159
/* magic string offset within super block */
160
#define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic)
161
/* start of checksummed data within superblock */
162
78
#define BCACHE_SB_CSUMMED_START 8U
163
/* granularity of offset and length fields within superblock */
164
450
#define BCACHEFS_SECTOR_SIZE   512U
165
/* maximum superblock size shift */
166
107
#define BCACHEFS_SB_MAX_SIZE_SHIFT   0x10U
167
/* fields offset within super block */
168
171
#define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start)
169
/* tag value for members field */
170
162
#define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1
171
/* tag value for disk_groups field */
172
162
#define BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS 5
173
/* version splitting helpers */
174
64
#define BCH_VERSION_MAJOR(_v)           ((uint16_t) ((_v) >> 10))
175
64
#define BCH_VERSION_MINOR(_v)           ((uint16_t) ((_v) & ~(~0U << 10)))
176
177
553
#define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8))
178
179
static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
180
    const struct bcache_super_block *bcs)
181
131
{
182
131
  const unsigned char *csummed;
183
131
  size_t csummed_size;
184
131
  uint64_t csum;
185
186
131
  if (le16_to_cpu(bcs->keys) > ARRAY_SIZE(bcs->d))
187
92
    return 0;
188
189
  /* up to the end of bcs->d[] */
190
39
  csummed_size = offsetof(__typeof__(*bcs), d) +
191
39
    sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys);
192
39
  csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
193
39
  csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START,
194
39
         csummed_size - BCACHE_SB_CSUMMED_START);
195
39
  return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum));
196
131
}
197
198
static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
199
131
{
200
131
  const struct bcache_super_block *bcs;
201
202
131
  bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block);
203
131
  if (!bcs)
204
0
    return errno ? -errno : BLKID_PROBE_NONE;
205
206
131
  if (!bcache_verify_checksum(pr, mag, bcs))
207
129
    return BLKID_PROBE_NONE;
208
209
2
  if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
210
2
    return BLKID_PROBE_NONE;
211
212
0
  if (blkid_probe_sprintf_version(pr, "%"PRIu64, le64_to_cpu(bcs->version)) < 0)
213
0
    return BLKID_PROBE_NONE;
214
215
0
  if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
216
0
    return BLKID_PROBE_NONE;
217
218
0
  if (blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)) < 0)
219
0
    return BLKID_PROBE_NONE;
220
221
0
  if (blkid_probe_set_block_size(pr, le16_to_cpu(bcs->block_size) * 512))
222
0
    return BLKID_PROBE_NONE;
223
224
0
  blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
225
226
0
  return BLKID_PROBE_OK;
227
0
}
228
229
static void probe_bcachefs_sb_members(blkid_probe pr,
230
             const struct bcachefs_super_block *bcs,
231
             const struct bcachefs_sb_field *field,
232
             uint8_t dev_idx)
233
50
{
234
50
  struct bcachefs_sb_field_members *members =
235
50
      (struct bcachefs_sb_field_members *) field;
236
50
  uint64_t sectors = 0;
237
50
  uint8_t i;
238
239
50
  if (BYTES(field) != offsetof(__typeof__(*members), members[bcs->nr_devices]))
240
50
    return;
241
242
0
  blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB");
243
244
0
  for (i = 0; i < bcs->nr_devices; i++) {
245
0
    struct bcachefs_sb_member *member = &members->members[i];
246
0
    sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size);
247
0
  }
248
0
  blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE);
249
0
}
250
251
static void probe_bcachefs_sb_disk_groups(blkid_probe pr,
252
            const struct bcachefs_super_block *bcs,
253
            const struct bcachefs_sb_field *field,
254
            uint8_t dev_idx)
255
8
{
256
8
  struct bcachefs_sb_field_disk_groups *disk_groups =
257
8
      (struct bcachefs_sb_field_disk_groups *) field;
258
259
8
  if (BYTES(field) != offsetof(__typeof__(*disk_groups), disk_groups[bcs->nr_devices]))
260
8
    return;
261
262
0
  blkid_probe_set_id_label(pr, "LABEL_SUB",
263
0
         disk_groups->disk_groups[dev_idx].label,
264
0
         sizeof(disk_groups->disk_groups[dev_idx].label));
265
0
}
266
267
static int is_within_range(const void *start, uint64_t size, const void *end)
268
444
{
269
444
  ptrdiff_t diff;
270
271
444
  if (start >= end)
272
0
    return 0; // should not happen
273
274
444
  diff = (unsigned char *) end - (unsigned char *) start;
275
444
  return size <= (uint64_t) diff;
276
444
}
277
278
static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs,
279
             const unsigned char *sb_start, const unsigned char *sb_end)
280
64
{
281
64
  const unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
282
283
226
  while (1) {
284
226
    struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr;
285
226
    uint64_t field_size;
286
226
    uint32_t type;
287
288
226
    if (!is_within_range(field, sizeof(*field), sb_end))
289
0
      break;
290
291
226
    field_size = BYTES(field);
292
293
226
    if (field_size < sizeof(*field))
294
8
      break;
295
296
218
    if (!is_within_range(field, field_size, sb_end))
297
53
      break;
298
299
165
    type = le32_to_cpu(field->type);
300
165
    if (!type)
301
3
      break;
302
303
162
    if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS)
304
50
      probe_bcachefs_sb_members(pr, bcs, field, bcs->dev_idx);
305
306
162
    if (type == BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS)
307
8
      probe_bcachefs_sb_disk_groups(pr, bcs, field, bcs->dev_idx);
308
309
162
    field_addr += BYTES(field);
310
162
  }
311
64
}
312
313
static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs,
314
              const unsigned char *sb, const unsigned char *sb_end)
315
87
{
316
87
  uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58;
317
87
  const unsigned char *checksummed_data_start = sb + sizeof(bcs->csum);
318
87
  size_t checksummed_data_size = sb_end - checksummed_data_start;
319
320
87
  switch (checksum_type) {
321
2
    case BCACHEFS_SB_CSUM_TYPE_NONE:
322
2
      return 1;
323
3
    case BCACHEFS_SB_CSUM_TYPE_CRC32C: {
324
3
      uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL;
325
3
      return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c));
326
0
    }
327
2
    case BCACHEFS_SB_CSUM_TYPE_CRC64: {
328
2
      uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size);
329
2
      return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64));
330
0
    }
331
18
    case BCACHEFS_SB_CSUM_TYPE_XXHASH: {
332
18
      XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0);
333
18
      return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64));
334
0
    }
335
62
    default:
336
62
      DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type));
337
62
      return 1;
338
87
  }
339
87
}
340
341
static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag)
342
220
{
343
220
  const struct bcachefs_super_block *bcs;
344
220
  const unsigned char *sb, *sb_end;
345
220
  uint64_t sb_size, blocksize, offset_sectors;
346
220
  uint16_t version;
347
348
220
  bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block);
349
220
  if (!bcs)
350
0
    return errno ? -errno : BLKID_PROBE_NONE;
351
352
220
  offset_sectors = blkid_probe_get_idmag_off(pr, mag) / BCACHEFS_SECTOR_SIZE;
353
220
  if (le64_to_cpu(bcs->offset) != offset_sectors)
354
103
    return BLKID_PROBE_NONE;
355
356
117
  if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices)
357
10
    return BLKID_PROBE_NONE;
358
359
107
  sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs);
360
361
107
  if (bcs->layout.sb_max_size_bits > BCACHEFS_SB_MAX_SIZE_SHIFT)
362
5
    return BLKID_PROBE_NONE;
363
364
102
  if (sb_size > (BCACHEFS_SECTOR_SIZE << bcs->layout.sb_max_size_bits))
365
13
    return BLKID_PROBE_NONE;
366
367
89
  sb = blkid_probe_get_sb_buffer(pr, mag, sb_size);
368
89
  if (!sb)
369
2
    return BLKID_PROBE_NONE;
370
87
  sb_end = sb + sb_size;
371
372
87
  if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end))
373
23
    return BLKID_PROBE_NONE;
374
375
64
  blkid_probe_set_uuid(pr, bcs->user_uuid);
376
64
  blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label));
377
64
  version = le16_to_cpu(bcs->version);
378
64
  blkid_probe_sprintf_version(pr, "%"PRIu16".%"PRIu16,
379
64
            BCH_VERSION_MAJOR(version),
380
64
            BCH_VERSION_MINOR(version));
381
64
  blocksize = le16_to_cpu(bcs->block_size);
382
64
  blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE);
383
64
  blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE);
384
64
  blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
385
386
64
  probe_bcachefs_sb_fields(pr, bcs, sb, sb_end);
387
388
64
  return BLKID_PROBE_OK;
389
87
}
390
391
const struct blkid_idinfo bcache_idinfo =
392
{
393
  .name   = "bcache",
394
  .usage    = BLKID_USAGE_OTHER,
395
  .probefunc  = probe_bcache,
396
  .minsz    = 8192,
397
  .magics   =
398
  {
399
    {
400
      .magic = BCACHE_SB_MAGIC,
401
      .len   = BCACHE_SB_MAGIC_LEN,
402
      .kboff = BCACHE_SB_KBOFF,
403
      .sboff = BCACHE_SB_MAGIC_OFF
404
    },
405
    { NULL }
406
  }
407
};
408
409
const struct blkid_idinfo bcachefs_idinfo =
410
{
411
  .name   = "bcachefs",
412
  .usage    = BLKID_USAGE_FILESYSTEM,
413
  .probefunc  = probe_bcachefs,
414
  .minsz    = 256 * BCACHEFS_SECTOR_SIZE,
415
  .magics   = {
416
    {
417
      .magic = BCACHE_SB_MAGIC,
418
      .len   = BCACHE_SB_MAGIC_LEN,
419
      .kboff = BCACHE_SB_KBOFF,
420
      .sboff = BCACHE_SB_MAGIC_OFF,
421
    },
422
    {
423
      .magic = BCACHEFS_SB_MAGIC,
424
      .len   = BCACHE_SB_MAGIC_LEN,
425
      .kboff = BCACHE_SB_KBOFF,
426
      .sboff = BCACHE_SB_MAGIC_OFF,
427
    },
428
    {
429
      .magic = BCACHEFS_SB_MAGIC,
430
      .len   = BCACHE_SB_MAGIC_LEN,
431
      .kboff = 1 << 11,
432
      .sboff = BCACHE_SB_MAGIC_OFF,
433
    },
434
    {
435
      .magic = BCACHEFS_SB_MAGIC,
436
      .len   = BCACHE_SB_MAGIC_LEN,
437
      .kboff = -(1 << 10),
438
      .sboff = BCACHE_SB_MAGIC_OFF,
439
    },
440
    { NULL }
441
  }
442
};