2 * Copyright (C) 2013 Rolf Fokkens <rolf@fokkens.nl>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
7 * Based on code fragments from bcache-tools by Kent Overstreet:
8 * http://evilpiepirate.org/git/bcache-tools.git
15 #include "superblocks.h"
20 #define SB_LABEL_SIZE 32
21 #define SB_JOURNAL_BUCKETS 256U
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
27 struct bcache_super_block
{
29 uint64_t offset
; /* where this super block was written */
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
];
38 uint64_t feature_compat
;
39 uint64_t feature_incompat
;
40 uint64_t feature_ro_compat
;
47 uint64_t nbuckets
; /* device size */
49 uint16_t block_size
; /* sectors */
50 uint16_t bucket_size
; /* sectors */
63 uint16_t first_bucket
;
65 uint16_t njournal_buckets
;
68 uint64_t d
[SB_JOURNAL_BUCKETS
]; /* journal buckets */
69 uint16_t obso_bucket_size_hi
; /* obsoleted */
70 } __attribute__((packed
));
72 struct bcachefs_sb_field
{
75 } __attribute__((packed
));
77 struct bcachefs_sb_member
{
80 uint16_t first_bucket
;
85 } __attribute__((packed
));
87 struct bcachefs_sb_field_members
{
88 struct bcachefs_sb_field field
;
89 struct bcachefs_sb_member members
[];
90 } __attribute__((packed
));
92 struct bcachefs_sb_disk_group
{
93 uint8_t label
[SB_LABEL_SIZE
];
95 } __attribute__((packed
));
97 struct bcachefs_sb_field_disk_groups
{
98 struct bcachefs_sb_field field
;
99 struct bcachefs_sb_disk_group disk_groups
[];
100 } __attribute__((packed
));
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,
109 union bcachefs_sb_csum
{
114 } __attribute__((packed
));
116 struct bcachefs_sb_layout
{
119 uint8_t sb_max_size_bits
;
120 uint8_t nr_superblocks
;
122 uint64_t sb_offset
[61];
123 } __attribute__((packed
));
125 struct bcachefs_super_block
{
126 union bcachefs_sb_csum csum
;
128 uint16_t version_min
;
132 uint8_t user_uuid
[16];
133 uint8_t label
[SB_LABEL_SIZE
];
140 uint64_t time_base_lo
;
141 uint32_t time_base_hi
;
142 uint32_t time_precision
;
144 uint64_t features
[2];
146 struct bcachefs_sb_layout layout
;
147 struct bcachefs_sb_field _start
[];
148 } __attribute__((packed
));
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 #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 #define BCACHE_SB_CSUMMED_START 8U
163 /* granularity of offset and length fields within superblock */
164 #define BCACHEFS_SECTOR_SIZE 512U
165 /* maximum superblock size shift */
166 #define BCACHEFS_SB_MAX_SIZE_SHIFT 0x10U
167 /* maximum superblock size */
168 #define BCACHEFS_SB_MAX_SIZE (1U << BCACHEFS_SB_MAX_SIZE_SHIFT)
169 /* fields offset within super block */
170 #define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start)
171 /* tag value for members field */
172 #define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1
173 /* tag value for disk_groups field */
174 #define BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS 5
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)))
179 #define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8))
181 static int bcache_verify_checksum(blkid_probe pr
, const struct blkid_idmag
*mag
,
182 const struct bcache_super_block
*bcs
)
184 const unsigned char *csummed
;
188 if (le16_to_cpu(bcs
->keys
) > ARRAY_SIZE(bcs
->d
))
191 /* up to the end of bcs->d[] */
192 csummed_size
= offsetof(__typeof__(*bcs
), d
) +
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
);
197 return blkid_probe_verify_csum(pr
, csum
, le64_to_cpu(bcs
->csum
));
200 static int probe_bcache (blkid_probe pr
, const struct blkid_idmag
*mag
)
202 struct bcache_super_block
*bcs
;
204 bcs
= blkid_probe_get_sb(pr
, mag
, struct bcache_super_block
);
206 return errno
? -errno
: BLKID_PROBE_NONE
;
208 if (!bcache_verify_checksum(pr
, mag
, bcs
))
209 return BLKID_PROBE_NONE
;
211 if (le64_to_cpu(bcs
->offset
) != BCACHE_SB_OFF
/ 512)
212 return BLKID_PROBE_NONE
;
214 if (blkid_probe_sprintf_version(pr
, "%"PRIu64
, le64_to_cpu(bcs
->version
)) < 0)
215 return BLKID_PROBE_NONE
;
217 if (blkid_probe_set_uuid(pr
, bcs
->uuid
) < 0)
218 return BLKID_PROBE_NONE
;
220 if (blkid_probe_set_label(pr
, bcs
->label
, sizeof(bcs
->label
)) < 0)
221 return BLKID_PROBE_NONE
;
223 if (blkid_probe_set_block_size(pr
, le16_to_cpu(bcs
->block_size
) * 512))
224 return BLKID_PROBE_NONE
;
226 blkid_probe_set_wiper(pr
, 0, BCACHE_SB_OFF
);
228 return BLKID_PROBE_OK
;
231 static void probe_bcachefs_sb_members(blkid_probe pr
,
232 const struct bcachefs_super_block
*bcs
,
233 const struct bcachefs_sb_field
*field
,
236 struct bcachefs_sb_field_members
*members
=
237 (struct bcachefs_sb_field_members
*) field
;
238 uint64_t sectors
= 0;
241 if (BYTES(field
) != offsetof(__typeof__(*members
), members
[bcs
->nr_devices
]))
244 blkid_probe_set_uuid_as(pr
, members
->members
[dev_idx
].uuid
, "UUID_SUB");
246 for (i
= 0; i
< bcs
->nr_devices
; i
++) {
247 struct bcachefs_sb_member
*member
= &members
->members
[i
];
248 sectors
+= le64_to_cpu(member
->nbuckets
) * le16_to_cpu(member
->bucket_size
);
250 blkid_probe_set_fssize(pr
, sectors
* BCACHEFS_SECTOR_SIZE
);
253 static void probe_bcachefs_sb_disk_groups(blkid_probe pr
,
254 const struct bcachefs_super_block
*bcs
,
255 const struct bcachefs_sb_field
*field
,
258 struct bcachefs_sb_field_disk_groups
*disk_groups
=
259 (struct bcachefs_sb_field_disk_groups
*) field
;
261 if (BYTES(field
) != offsetof(__typeof__(*disk_groups
), disk_groups
[bcs
->nr_devices
]))
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
));
269 static int is_within_range(const void *start
, uint64_t size
, const void *end
)
274 return 0; // should not happen
276 diff
= (unsigned char *) end
- (unsigned char *) start
;
277 return size
<= (uint64_t) diff
;
280 static void probe_bcachefs_sb_fields(blkid_probe pr
, const struct bcachefs_super_block
*bcs
,
281 const unsigned char *sb_start
, const unsigned char *sb_end
)
283 const unsigned char *field_addr
= sb_start
+ BCACHEFS_SB_FIELDS_OFF
;
286 struct bcachefs_sb_field
*field
= (struct bcachefs_sb_field
*) field_addr
;
290 if (!is_within_range(field
, sizeof(*field
), sb_end
))
293 field_size
= BYTES(field
);
295 if (field_size
< sizeof(*field
))
298 if (!is_within_range(field
, field_size
, sb_end
))
301 type
= le32_to_cpu(field
->type
);
305 if (type
== BCACHEFS_SB_FIELD_TYPE_MEMBERS
)
306 probe_bcachefs_sb_members(pr
, bcs
, field
, bcs
->dev_idx
);
308 if (type
== BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS
)
309 probe_bcachefs_sb_disk_groups(pr
, bcs
, field
, bcs
->dev_idx
);
311 field_addr
+= BYTES(field
);
315 static int bcachefs_validate_checksum(blkid_probe pr
, const struct bcachefs_super_block
*bcs
,
316 const unsigned char *sb
, const unsigned char *sb_end
)
318 uint8_t checksum_type
= be64_to_cpu(bcs
->flags
[0]) >> 58;
319 const unsigned char *checksummed_data_start
= sb
+ sizeof(bcs
->csum
);
320 size_t checksummed_data_size
= sb_end
- checksummed_data_start
;
322 switch (checksum_type
) {
323 case BCACHEFS_SB_CSUM_TYPE_NONE
:
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
));
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
));
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
));
338 DBG(LOWPROBE
, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type
));
343 static int probe_bcachefs(blkid_probe pr
, const struct blkid_idmag
*mag
)
345 struct bcachefs_super_block
*bcs
;
346 const unsigned char *sb
, *sb_end
;
347 uint64_t sb_size
, blocksize
, offset_sectors
;
350 bcs
= blkid_probe_get_sb(pr
, mag
, struct bcachefs_super_block
);
352 return errno
? -errno
: BLKID_PROBE_NONE
;
354 offset_sectors
= blkid_probe_get_idmag_off(pr
, mag
) / BCACHEFS_SECTOR_SIZE
;
355 if (le64_to_cpu(bcs
->offset
) != offset_sectors
)
356 return BLKID_PROBE_NONE
;
358 if (bcs
->nr_devices
== 0 || bcs
->dev_idx
>= bcs
->nr_devices
)
359 return BLKID_PROBE_NONE
;
361 sb_size
= BCACHEFS_SB_FIELDS_OFF
+ BYTES(bcs
);
363 if (sb_size
> BCACHEFS_SB_MAX_SIZE
)
364 return BLKID_PROBE_NONE
;
366 if (bcs
->layout
.sb_max_size_bits
> BCACHEFS_SB_MAX_SIZE_SHIFT
)
367 return BLKID_PROBE_NONE
;
369 if (sb_size
> (BCACHEFS_SECTOR_SIZE
<< bcs
->layout
.sb_max_size_bits
))
370 return BLKID_PROBE_NONE
;
372 sb
= blkid_probe_get_sb_buffer(pr
, mag
, sb_size
);
374 return BLKID_PROBE_NONE
;
375 sb_end
= sb
+ sb_size
;
377 if (!bcachefs_validate_checksum(pr
, bcs
, sb
, sb_end
))
378 return BLKID_PROBE_NONE
;
380 blkid_probe_set_uuid(pr
, bcs
->user_uuid
);
381 blkid_probe_set_label(pr
, bcs
->label
, sizeof(bcs
->label
));
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
));
386 blocksize
= le16_to_cpu(bcs
->block_size
);
387 blkid_probe_set_block_size(pr
, blocksize
* BCACHEFS_SECTOR_SIZE
);
388 blkid_probe_set_fsblocksize(pr
, blocksize
* BCACHEFS_SECTOR_SIZE
);
389 blkid_probe_set_wiper(pr
, 0, BCACHE_SB_OFF
);
391 probe_bcachefs_sb_fields(pr
, bcs
, sb
, sb_end
);
393 return BLKID_PROBE_OK
;
396 const struct blkid_idinfo bcache_idinfo
=
399 .usage
= BLKID_USAGE_OTHER
,
400 .probefunc
= probe_bcache
,
405 .magic
= BCACHE_SB_MAGIC
,
406 .len
= BCACHE_SB_MAGIC_LEN
,
407 .kboff
= BCACHE_SB_KBOFF
,
408 .sboff
= BCACHE_SB_MAGIC_OFF
414 const struct blkid_idinfo bcachefs_idinfo
=
417 .usage
= BLKID_USAGE_FILESYSTEM
,
418 .probefunc
= probe_bcachefs
,
419 .minsz
= 256 * BCACHEFS_SECTOR_SIZE
,
422 .magic
= BCACHE_SB_MAGIC
,
423 .len
= BCACHE_SB_MAGIC_LEN
,
424 .kboff
= BCACHE_SB_KBOFF
,
425 .sboff
= BCACHE_SB_MAGIC_OFF
,
428 .magic
= BCACHEFS_SB_MAGIC
,
429 .len
= BCACHE_SB_MAGIC_LEN
,
430 .kboff
= BCACHE_SB_KBOFF
,
431 .sboff
= BCACHE_SB_MAGIC_OFF
,
434 .magic
= BCACHEFS_SB_MAGIC
,
435 .len
= BCACHE_SB_MAGIC_LEN
,
437 .sboff
= BCACHE_SB_MAGIC_OFF
,