/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 "buffer.h" |
16 | | #include "superblocks.h" |
17 | | #include "crc32c.h" |
18 | | #include "crc64.h" |
19 | | #include "xxhash.h" |
20 | | |
21 | | #define SB_LABEL_SIZE 32 |
22 | | #define SB_JOURNAL_BUCKETS 256U |
23 | | |
24 | | /* |
25 | | * The bcache_super_block is adapted from struct cache_sb in kernel. |
26 | | * https://github.com/torvalds/linux/blob/master/drivers/md/bcache/bcache_ondisk.h |
27 | | */ |
28 | | struct bcache_super_block { |
29 | | uint64_t csum; |
30 | | uint64_t offset; /* where this super block was written */ |
31 | | uint64_t version; |
32 | | uint8_t magic[16]; /* bcache file system identifier */ |
33 | | uint8_t uuid[16]; /* device identifier */ |
34 | | uint8_t set_info[16]; /* magic or uuid */ |
35 | | uint8_t label[SB_LABEL_SIZE]; |
36 | | uint64_t flags; |
37 | | uint64_t seq; |
38 | | |
39 | | uint64_t feature_compat; |
40 | | uint64_t feature_incompat; |
41 | | uint64_t feature_ro_compat; |
42 | | |
43 | | uint64_t pad[5]; |
44 | | |
45 | | union { |
46 | | struct { |
47 | | /* Cache devices */ |
48 | | uint64_t nbuckets; /* device size */ |
49 | | |
50 | | uint16_t block_size; /* sectors */ |
51 | | uint16_t bucket_size; /* sectors */ |
52 | | |
53 | | uint16_t nr_in_set; |
54 | | uint16_t nr_this_dev; |
55 | | }; |
56 | | struct { |
57 | | /* Backing devices */ |
58 | | uint64_t data_offset; |
59 | | }; |
60 | | }; |
61 | | |
62 | | uint32_t last_mount; |
63 | | |
64 | | uint16_t first_bucket; |
65 | | union { |
66 | | uint16_t njournal_buckets; |
67 | | uint16_t keys; |
68 | | }; |
69 | | uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */ |
70 | | uint16_t obso_bucket_size_hi; /* obsoleted */ |
71 | | } __attribute__((packed)); |
72 | | |
73 | | struct bcachefs_sb_field { |
74 | | uint32_t u64s; |
75 | | uint32_t type; |
76 | | } __attribute__((packed)); |
77 | | |
78 | | struct bcachefs_sb_member { |
79 | | uint8_t uuid[16]; |
80 | | uint64_t nbuckets; |
81 | | uint16_t first_bucket; |
82 | | uint16_t bucket_size; |
83 | | uint8_t btree_bitmap_shift; |
84 | | uint8_t pad[3]; |
85 | | uint64_t last_mount; |
86 | | uint64_t flags; |
87 | | /* fields below were added after members_v1 */ |
88 | | uint32_t iops[4]; |
89 | | uint64_t errors[3]; |
90 | | uint64_t errors_at_reset[3]; |
91 | | uint64_t errors_reset_time; |
92 | | uint64_t seq; |
93 | | uint64_t btree_allocated_bitmap; |
94 | | uint32_t last_journal_bucket; |
95 | | uint32_t last_journal_bucket_offset; |
96 | | |
97 | | /* device name/model info from the last FS mount */ |
98 | | uint8_t device_name[16]; |
99 | | uint8_t device_model[64]; |
100 | | } __attribute__((packed)); |
101 | | |
102 | | struct bcachefs_sb_field_members_v1 { |
103 | | struct bcachefs_sb_field field; |
104 | | struct bcachefs_sb_member members[]; |
105 | | } __attribute__((packed)); |
106 | | |
107 | | struct bcachefs_sb_field_members_v2 { |
108 | | struct bcachefs_sb_field field; |
109 | | uint16_t member_bytes; |
110 | | uint8_t pad[6]; |
111 | | struct bcachefs_sb_member members[]; |
112 | | } __attribute__((packed)); |
113 | | |
114 | | struct bcachefs_sb_disk_group { |
115 | | uint8_t label[SB_LABEL_SIZE]; |
116 | | uint64_t flags[2]; |
117 | | } __attribute__((packed)); |
118 | | |
119 | | struct bcachefs_sb_field_disk_groups { |
120 | | struct bcachefs_sb_field field; |
121 | | struct bcachefs_sb_disk_group disk_groups[]; |
122 | | } __attribute__((packed)); |
123 | | |
124 | | enum bcachefs_sb_csum_type { |
125 | | BCACHEFS_SB_CSUM_TYPE_NONE = 0, |
126 | | BCACHEFS_SB_CSUM_TYPE_CRC32C = 1, |
127 | | BCACHEFS_SB_CSUM_TYPE_CRC64 = 2, |
128 | | BCACHEFS_SB_CSUM_TYPE_XXHASH = 7, |
129 | | }; |
130 | | |
131 | | union bcachefs_sb_csum { |
132 | | uint32_t crc32c; |
133 | | uint64_t crc64; |
134 | | XXH64_hash_t xxh64; |
135 | | uint8_t raw[16]; |
136 | | } __attribute__((packed)); |
137 | | |
138 | | struct bcachefs_sb_layout { |
139 | | uint8_t magic[16]; |
140 | | uint8_t layout_type; |
141 | | uint8_t sb_max_size_bits; |
142 | | uint8_t nr_superblocks; |
143 | | uint8_t pad[5]; |
144 | | uint64_t sb_offset[61]; |
145 | | } __attribute__((packed)); |
146 | | |
147 | | struct bcachefs_super_block { |
148 | | union bcachefs_sb_csum csum; |
149 | | uint16_t version; |
150 | | uint16_t version_min; |
151 | | uint16_t pad[2]; |
152 | | uint8_t magic[16]; |
153 | | uint8_t uuid[16]; |
154 | | uint8_t user_uuid[16]; |
155 | | uint8_t label[SB_LABEL_SIZE]; |
156 | | uint64_t offset; |
157 | | uint64_t seq; |
158 | | uint16_t block_size; |
159 | | uint8_t dev_idx; |
160 | | uint8_t nr_devices; |
161 | | uint32_t u64s; |
162 | | uint64_t time_base_lo; |
163 | | uint32_t time_base_hi; |
164 | | uint32_t time_precision; |
165 | | uint64_t flags[8]; |
166 | | uint64_t features[2]; |
167 | | uint64_t compat[2]; |
168 | | struct bcachefs_sb_layout layout; |
169 | | struct bcachefs_sb_field _start[]; |
170 | | } __attribute__((packed)); |
171 | | |
172 | | /* magic string */ |
173 | | #define BCACHE_SB_MAGIC "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81" |
174 | | #define BCACHEFS_SB_MAGIC "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef" |
175 | | /* magic string len */ |
176 | | #define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1) |
177 | | /* super block offset */ |
178 | 721 | #define BCACHE_SB_OFF 0x1000U |
179 | | /* supper block offset in kB */ |
180 | | #define BCACHE_SB_KBOFF (BCACHE_SB_OFF >> 10) |
181 | | /* magic string offset within super block */ |
182 | | #define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic) |
183 | | /* start of checksummed data within superblock */ |
184 | 924 | #define BCACHE_SB_CSUMMED_START 8U |
185 | | /* granularity of offset and length fields within superblock */ |
186 | 3.53k | #define BCACHEFS_SECTOR_SIZE 512U |
187 | | /* maximum superblock size shift */ |
188 | 278 | #define BCACHEFS_SB_MAX_SIZE_SHIFT 0x10U |
189 | | /* fields offset within super block */ |
190 | 532 | #define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start) |
191 | | /* tag value for v1 members field */ |
192 | 25.3k | #define BCACHEFS_SB_FIELD_TYPE_MEMBERS_V1 1 |
193 | | /* v1 members field size in bytes */ |
194 | 5.75k | #define BCACHEFS_SB_FIELD_MEMBERS_V1_BYTES 56 |
195 | | /* tag value for v2 members field */ |
196 | 25.3k | #define BCACHEFS_SB_FIELD_TYPE_MEMBERS_V2 11 |
197 | | /* tag value for disk_groups field */ |
198 | 25.3k | #define BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS 5 |
199 | | /* version splitting helpers */ |
200 | 254 | #define BCH_VERSION_MAJOR(_v) ((uint16_t) ((_v) >> 10)) |
201 | 254 | #define BCH_VERSION_MINOR(_v) ((uint16_t) ((_v) & ~(~0U << 10))) |
202 | | |
203 | 57.3k | #define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8)) |
204 | | |
205 | | static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, |
206 | | const struct bcache_super_block *bcs) |
207 | 657 | { |
208 | 657 | const unsigned char *csummed; |
209 | 657 | size_t csummed_size; |
210 | 657 | uint64_t csum; |
211 | | |
212 | 657 | if (le16_to_cpu(bcs->keys) > ARRAY_SIZE(bcs->d)) |
213 | 195 | return 0; |
214 | | |
215 | | /* up to the end of bcs->d[] */ |
216 | 462 | csummed_size = offsetof(__typeof__(*bcs), d) + |
217 | 462 | sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys); |
218 | 462 | csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); |
219 | 462 | if (!csummed) |
220 | 0 | return 0; |
221 | 462 | csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START, |
222 | 462 | csummed_size - BCACHE_SB_CSUMMED_START); |
223 | 462 | return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum)); |
224 | 462 | } |
225 | | |
226 | | static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag) |
227 | 657 | { |
228 | 657 | const struct bcache_super_block *bcs; |
229 | | |
230 | 657 | bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block); |
231 | 657 | if (!bcs) |
232 | 0 | return errno ? -errno : BLKID_PROBE_NONE; |
233 | | |
234 | 657 | if (!bcache_verify_checksum(pr, mag, bcs)) |
235 | 195 | return BLKID_PROBE_NONE; |
236 | | |
237 | 462 | if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512) |
238 | 457 | return BLKID_PROBE_NONE; |
239 | | |
240 | 5 | if (blkid_probe_sprintf_version(pr, "%"PRIu64, le64_to_cpu(bcs->version)) < 0) |
241 | 0 | return BLKID_PROBE_NONE; |
242 | | |
243 | 5 | if (blkid_probe_set_uuid(pr, bcs->uuid) < 0) |
244 | 0 | return BLKID_PROBE_NONE; |
245 | | |
246 | 5 | if (blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)) < 0) |
247 | 0 | return BLKID_PROBE_NONE; |
248 | | |
249 | 5 | if (blkid_probe_set_block_size(pr, le16_to_cpu(bcs->block_size) * 512)) |
250 | 0 | return BLKID_PROBE_NONE; |
251 | | |
252 | 5 | blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF); |
253 | | |
254 | 5 | return BLKID_PROBE_OK; |
255 | 5 | } |
256 | | |
257 | 8.83k | #define FIELD_END(s, field) offsetof(s, field) + sizeof_member(s, field) |
258 | | |
259 | | /* Fields must be dynamically checked for existence before accessing (using FIELD_END) */ |
260 | | static const struct bcachefs_sb_member *bcachefs_sb_member_get_unsafe( |
261 | | const struct bcachefs_sb_member *members, |
262 | | uint16_t member_bytes, size_t i) |
263 | 7.71k | { |
264 | 7.71k | return (const void*)((const char *) members + i * member_bytes); |
265 | 7.71k | } |
266 | | |
267 | | static void probe_bcachefs_sb_members(blkid_probe pr, |
268 | | const struct bcachefs_super_block *bcs, |
269 | | const struct bcachefs_sb_field *field, |
270 | | const struct bcachefs_sb_member *members, |
271 | | uint16_t member_bytes, size_t member_array_offset, |
272 | | uint8_t *current_member_group) |
273 | 6.12k | { |
274 | 6.12k | uint64_t sectors = 0; |
275 | 6.12k | uint8_t i; |
276 | 6.12k | const struct bcachefs_sb_member *current; |
277 | | |
278 | 6.12k | if (BYTES(field) != member_array_offset + bcs->nr_devices * member_bytes) |
279 | 3.79k | return; |
280 | | |
281 | 2.32k | current = bcachefs_sb_member_get_unsafe(members, member_bytes, bcs->dev_idx); |
282 | | |
283 | 2.32k | if (member_bytes < FIELD_END(struct bcachefs_sb_member, uuid)) |
284 | 158 | return; |
285 | | |
286 | 2.16k | blkid_probe_set_uuid_as(pr, current->uuid, "UUID_SUB"); |
287 | | |
288 | 2.16k | if (member_bytes < FIELD_END(struct bcachefs_sb_member, nbuckets) || |
289 | 2.16k | member_bytes < FIELD_END(struct bcachefs_sb_member, bucket_size)) |
290 | 0 | return; |
291 | | |
292 | 7.55k | for (i = 0; i < bcs->nr_devices; i++) { |
293 | 5.38k | const struct bcachefs_sb_member *member = bcachefs_sb_member_get_unsafe(members, member_bytes, i); |
294 | 5.38k | sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size); |
295 | 5.38k | } |
296 | 2.16k | blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE); |
297 | | |
298 | 2.16k | if (member_bytes < FIELD_END(struct bcachefs_sb_member, flags)) |
299 | 0 | return; |
300 | | |
301 | 2.16k | *current_member_group = le64_to_cpu(current->flags) >> 20 & 0xFF; |
302 | 2.16k | } |
303 | | |
304 | | static void probe_bcachefs_device_label(blkid_probe pr, |
305 | | const struct bcachefs_sb_field *field, |
306 | | unsigned group_idx) |
307 | 52 | { |
308 | 52 | const struct bcachefs_sb_field_disk_groups *disk_groups = |
309 | 52 | (const struct bcachefs_sb_field_disk_groups *) field; |
310 | 52 | const struct bcachefs_sb_disk_group *group; |
311 | 52 | size_t groups_nr = (BYTES(field) - offsetof(__typeof__(*disk_groups), disk_groups)) / sizeof(*group); |
312 | 52 | size_t nr = 0; |
313 | 52 | size_t total_sublabel_length = 0; |
314 | 52 | uint16_t path[32]; |
315 | 52 | struct ul_buffer buf = UL_INIT_BUFFER; |
316 | | |
317 | 140 | while (1) { |
318 | 140 | if (nr == ARRAY_SIZE(path) || group_idx >= groups_nr) |
319 | 38 | return; |
320 | | |
321 | 102 | group = disk_groups->disk_groups + group_idx; |
322 | | |
323 | 102 | uint64_t group_flags_0 = le64_to_cpu(group->flags[0]); |
324 | 102 | bool deleted = group_flags_0 & 0x1; |
325 | 102 | uint32_t parent = group_flags_0 >> 6 & ((1 << 18) - 1); |
326 | | |
327 | 102 | if (deleted) |
328 | 3 | return; |
329 | | |
330 | 99 | path[nr++] = group_idx; |
331 | 99 | total_sublabel_length += strnlen((const char*)group->label, sizeof(group->label)); |
332 | | |
333 | 99 | if (!parent) |
334 | 11 | break; |
335 | 88 | group_idx = parent - 1; |
336 | 88 | } |
337 | | |
338 | 11 | if (ul_buffer_alloc_data(&buf, total_sublabel_length + nr + 1)) |
339 | 0 | return; |
340 | | |
341 | 34 | while (nr--) { |
342 | 23 | group_idx = path[nr]; |
343 | 23 | group = disk_groups->disk_groups + group_idx; |
344 | | |
345 | 23 | ul_buffer_append_data(&buf, (const char*)group->label, |
346 | 23 | strnlen((const char*)group->label, sizeof(group->label))); |
347 | 23 | ul_buffer_append_data(&buf, nr ? "." : "", 1); |
348 | 23 | } |
349 | | |
350 | 11 | if (!ul_buffer_is_empty(&buf)) { |
351 | 11 | blkid_probe_set_id_label(pr, "LABEL_SUB", |
352 | 11 | (const unsigned char*)ul_buffer_get_data(&buf, NULL, NULL), |
353 | 11 | ul_buffer_get_datasiz(&buf)); |
354 | 11 | } |
355 | | |
356 | 11 | ul_buffer_free_data(&buf); |
357 | 11 | } |
358 | | |
359 | | static int is_within_range(const void *start, uint64_t size, const void *end) |
360 | 51.1k | { |
361 | 51.1k | ptrdiff_t diff; |
362 | | |
363 | 51.1k | if (start >= end) |
364 | 6 | return 0; // should not happen |
365 | | |
366 | 51.0k | diff = (unsigned char *) end - (unsigned char *) start; |
367 | 51.0k | return size <= (uint64_t) diff; |
368 | 51.1k | } |
369 | | |
370 | | static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs, |
371 | | const unsigned char *sb_start, const unsigned char *sb_end) |
372 | 254 | { |
373 | 254 | const unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF; |
374 | 254 | const struct bcachefs_sb_field_disk_groups *disk_groups = NULL; |
375 | 254 | uint8_t current_member_group = 0; /* 0 means unset */ |
376 | | |
377 | 25.5k | while (1) { |
378 | 25.5k | struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr; |
379 | 25.5k | uint64_t field_size; |
380 | 25.5k | uint32_t type; |
381 | | |
382 | 25.5k | if (!is_within_range(field, sizeof(*field), sb_end)) |
383 | 6 | break; |
384 | | |
385 | 25.5k | field_size = BYTES(field); |
386 | | |
387 | 25.5k | if (field_size < sizeof(*field)) |
388 | 56 | break; |
389 | | |
390 | 25.5k | if (!is_within_range(field, field_size, sb_end)) |
391 | 185 | break; |
392 | | |
393 | 25.3k | type = le32_to_cpu(field->type); |
394 | 25.3k | if (!type) |
395 | 7 | break; |
396 | | |
397 | 25.3k | if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS_V1) { |
398 | 5.75k | struct bcachefs_sb_field_members_v1 *members = (struct bcachefs_sb_field_members_v1 *) field; |
399 | 5.75k | probe_bcachefs_sb_members(pr, bcs, field, members->members, |
400 | 5.75k | BCACHEFS_SB_FIELD_MEMBERS_V1_BYTES, offsetof(__typeof__(*members), members), |
401 | 5.75k | ¤t_member_group); |
402 | 5.75k | } |
403 | | |
404 | 25.3k | if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS_V2) { |
405 | 375 | struct bcachefs_sb_field_members_v2 *members = (struct bcachefs_sb_field_members_v2 *) field; |
406 | 375 | probe_bcachefs_sb_members(pr, bcs, field, members->members, |
407 | 375 | le16_to_cpu(members->member_bytes), offsetof(__typeof__(*members), members), |
408 | 375 | ¤t_member_group); |
409 | 375 | } |
410 | | |
411 | 25.3k | if (type == BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS) |
412 | 904 | disk_groups = (const struct bcachefs_sb_field_disk_groups *) field; |
413 | | |
414 | 25.3k | field_addr += BYTES(field); |
415 | 25.3k | } |
416 | | |
417 | 254 | if (disk_groups && current_member_group) |
418 | 52 | probe_bcachefs_device_label(pr, &disk_groups->field, current_member_group - 1); |
419 | 254 | } |
420 | | |
421 | | static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs, |
422 | | const unsigned char *sb, const unsigned char *sb_end) |
423 | 254 | { |
424 | 254 | uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58; |
425 | 254 | const unsigned char *checksummed_data_start = sb + sizeof(bcs->csum); |
426 | 254 | size_t checksummed_data_size = sb_end - checksummed_data_start; |
427 | | |
428 | 254 | switch (checksum_type) { |
429 | 8 | case BCACHEFS_SB_CSUM_TYPE_NONE: |
430 | 8 | return 1; |
431 | 12 | case BCACHEFS_SB_CSUM_TYPE_CRC32C: { |
432 | 12 | uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL; |
433 | 12 | return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c)); |
434 | 0 | } |
435 | 21 | case BCACHEFS_SB_CSUM_TYPE_CRC64: { |
436 | 21 | uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size); |
437 | 21 | return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64)); |
438 | 0 | } |
439 | 108 | case BCACHEFS_SB_CSUM_TYPE_XXHASH: { |
440 | 108 | XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0); |
441 | 108 | return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64)); |
442 | 0 | } |
443 | 105 | default: |
444 | 105 | DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type)); |
445 | 105 | return 1; |
446 | 254 | } |
447 | 254 | } |
448 | | |
449 | | static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag) |
450 | 585 | { |
451 | 585 | const struct bcachefs_super_block *bcs; |
452 | 585 | const unsigned char *sb, *sb_end; |
453 | 585 | uint64_t sb_size, blocksize, offset_sectors; |
454 | 585 | uint16_t version; |
455 | | |
456 | 585 | bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block); |
457 | 585 | if (!bcs) |
458 | 2 | return errno ? -errno : BLKID_PROBE_NONE; |
459 | | |
460 | 583 | offset_sectors = blkid_probe_get_idmag_off(pr, mag) / BCACHEFS_SECTOR_SIZE; |
461 | 583 | if (le64_to_cpu(bcs->offset) != offset_sectors) |
462 | 284 | return BLKID_PROBE_NONE; |
463 | | |
464 | 299 | if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices) |
465 | 21 | return BLKID_PROBE_NONE; |
466 | | |
467 | 278 | sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs); |
468 | | |
469 | 278 | if (bcs->layout.sb_max_size_bits > BCACHEFS_SB_MAX_SIZE_SHIFT) |
470 | 2 | return BLKID_PROBE_NONE; |
471 | | |
472 | 276 | if (sb_size > (BCACHEFS_SECTOR_SIZE << bcs->layout.sb_max_size_bits)) |
473 | 20 | return BLKID_PROBE_NONE; |
474 | | |
475 | 256 | sb = blkid_probe_get_sb_buffer(pr, mag, sb_size); |
476 | 256 | if (!sb) |
477 | 2 | return BLKID_PROBE_NONE; |
478 | 254 | sb_end = sb + sb_size; |
479 | | |
480 | 254 | if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end)) |
481 | 0 | return BLKID_PROBE_NONE; |
482 | | |
483 | 254 | blkid_probe_set_uuid(pr, bcs->user_uuid); |
484 | 254 | blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)); |
485 | 254 | version = le16_to_cpu(bcs->version); |
486 | 254 | blkid_probe_sprintf_version(pr, "%"PRIu16".%"PRIu16, |
487 | 254 | BCH_VERSION_MAJOR(version), |
488 | 254 | BCH_VERSION_MINOR(version)); |
489 | 254 | blocksize = le16_to_cpu(bcs->block_size); |
490 | 254 | blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE); |
491 | 254 | blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE); |
492 | 254 | blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF); |
493 | | |
494 | 254 | probe_bcachefs_sb_fields(pr, bcs, sb, sb_end); |
495 | | |
496 | 254 | return BLKID_PROBE_OK; |
497 | 254 | } |
498 | | |
499 | | const struct blkid_idinfo bcache_idinfo = |
500 | | { |
501 | | .name = "bcache", |
502 | | .usage = BLKID_USAGE_OTHER, |
503 | | .probefunc = probe_bcache, |
504 | | .minsz = 8192, |
505 | | .magics = |
506 | | { |
507 | | { |
508 | | .magic = BCACHE_SB_MAGIC, |
509 | | .len = BCACHE_SB_MAGIC_LEN, |
510 | | .kboff = BCACHE_SB_KBOFF, |
511 | | .sboff = BCACHE_SB_MAGIC_OFF |
512 | | }, |
513 | | { NULL } |
514 | | } |
515 | | }; |
516 | | |
517 | | const struct blkid_idinfo bcachefs_idinfo = |
518 | | { |
519 | | .name = "bcachefs", |
520 | | .usage = BLKID_USAGE_FILESYSTEM, |
521 | | .probefunc = probe_bcachefs, |
522 | | .minsz = 256 * BCACHEFS_SECTOR_SIZE, |
523 | | .magics = { |
524 | | { |
525 | | .magic = BCACHE_SB_MAGIC, |
526 | | .len = BCACHE_SB_MAGIC_LEN, |
527 | | .kboff = BCACHE_SB_KBOFF, |
528 | | .sboff = BCACHE_SB_MAGIC_OFF, |
529 | | }, |
530 | | { |
531 | | .magic = BCACHEFS_SB_MAGIC, |
532 | | .len = BCACHE_SB_MAGIC_LEN, |
533 | | .kboff = BCACHE_SB_KBOFF, |
534 | | .sboff = BCACHE_SB_MAGIC_OFF, |
535 | | }, |
536 | | { |
537 | | .magic = BCACHEFS_SB_MAGIC, |
538 | | .len = BCACHE_SB_MAGIC_LEN, |
539 | | .kboff = 1 << 11, |
540 | | .sboff = BCACHE_SB_MAGIC_OFF, |
541 | | }, |
542 | | { |
543 | | .magic = BCACHEFS_SB_MAGIC, |
544 | | .len = BCACHE_SB_MAGIC_LEN, |
545 | | .kboff = -(1 << 10), |
546 | | .sboff = BCACHE_SB_MAGIC_OFF, |
547 | | }, |
548 | | { NULL } |
549 | | } |
550 | | }; |