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 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,
99 union bcachefs_sb_csum
{
104 } __attribute__((packed
));
106 struct bcachefs_super_block
{
107 union bcachefs_sb_csum csum
;
109 uint16_t version_min
;
113 uint8_t user_uuid
[16];
114 uint8_t label
[SB_LABEL_SIZE
];
121 uint64_t time_base_lo
;
122 uint32_t time_base_hi
;
123 uint32_t time_precision
;
125 uint64_t features
[2];
128 struct bcachefs_sb_field _start
[];
129 } __attribute__((packed
));
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
153 #define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8))
155 static int bcache_verify_checksum(blkid_probe pr
, const struct blkid_idmag
*mag
,
156 const struct bcache_super_block
*bcs
)
158 const unsigned char *csummed
;
162 if (le16_to_cpu(bcs
->keys
) > ARRAY_SIZE(bcs
->d
))
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
));
174 static int probe_bcache (blkid_probe pr
, const struct blkid_idmag
*mag
)
176 struct bcache_super_block
*bcs
;
178 bcs
= blkid_probe_get_sb(pr
, mag
, struct bcache_super_block
);
180 return errno
? -errno
: BLKID_PROBE_NONE
;
182 if (!bcache_verify_checksum(pr
, mag
, bcs
))
183 return BLKID_PROBE_NONE
;
185 if (le64_to_cpu(bcs
->offset
) != BCACHE_SB_OFF
/ 512)
186 return BLKID_PROBE_NONE
;
188 if (blkid_probe_sprintf_version(pr
, "%"PRIu64
, le64_to_cpu(bcs
->version
)) < 0)
189 return BLKID_PROBE_NONE
;
191 if (blkid_probe_set_uuid(pr
, bcs
->uuid
) < 0)
192 return BLKID_PROBE_NONE
;
194 if (blkid_probe_set_label(pr
, bcs
->label
, sizeof(bcs
->label
)) < 0)
195 return BLKID_PROBE_NONE
;
197 blkid_probe_set_wiper(pr
, 0, BCACHE_SB_OFF
);
199 return BLKID_PROBE_OK
;
202 static void probe_bcachefs_sb_members(blkid_probe pr
,
203 const struct bcachefs_super_block
*bcs
,
204 const struct bcachefs_sb_field
*field
,
207 struct bcachefs_sb_field_members
*members
=
208 (struct bcachefs_sb_field_members
*) field
;
209 uint64_t sectors
= 0;
212 if (BYTES(field
) != offsetof(typeof(*members
), members
[bcs
->nr_devices
]))
215 blkid_probe_set_uuid_as(pr
, members
->members
[dev_idx
].uuid
, "UUID_SUB");
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
);
221 blkid_probe_set_fssize(pr
, sectors
* BCACHEFS_SECTOR_SIZE
);
224 static int is_within_range(const void *start
, uint64_t size
, const void *end
)
229 return 0; // should not happen
231 diff
= (unsigned char *) end
- (unsigned char *) start
;
232 return size
<= (uint64_t) diff
;
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
)
238 const unsigned char *field_addr
= sb_start
+ BCACHEFS_SB_FIELDS_OFF
;
241 struct bcachefs_sb_field
*field
= (struct bcachefs_sb_field
*) field_addr
;
245 if (!is_within_range(field
, sizeof(*field
), sb_end
))
248 field_size
= BYTES(field
);
250 if (field_size
< sizeof(*field
))
253 if (!is_within_range(field
, field_size
, sb_end
))
256 type
= le32_to_cpu(field
->type
);
260 if (type
== BCACHEFS_SB_FIELD_TYPE_MEMBERS
)
261 probe_bcachefs_sb_members(pr
, bcs
, field
, bcs
->dev_idx
);
263 field_addr
+= BYTES(field
);
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
)
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
;
274 switch (checksum_type
) {
275 case BCACHEFS_SB_CSUM_TYPE_NONE
:
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
));
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
));
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
));
290 DBG(LOWPROBE
, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type
));
295 static int probe_bcachefs(blkid_probe pr
, const struct blkid_idmag
*mag
)
297 struct bcachefs_super_block
*bcs
;
298 const unsigned char *sb
, *sb_end
;
299 uint64_t sb_size
, blocksize
;
301 bcs
= blkid_probe_get_sb(pr
, mag
, struct bcachefs_super_block
);
303 return errno
? -errno
: BLKID_PROBE_NONE
;
305 if (le64_to_cpu(bcs
->offset
) != BCACHE_SB_OFF
/ BCACHEFS_SECTOR_SIZE
)
306 return BLKID_PROBE_NONE
;
308 if (bcs
->nr_devices
== 0 || bcs
->dev_idx
>= bcs
->nr_devices
)
309 return BLKID_PROBE_NONE
;
311 sb_size
= BCACHEFS_SB_FIELDS_OFF
+ BYTES(bcs
);
312 if (sb_size
> BCACHEFS_SB_MAX_SIZE
)
313 return BLKID_PROBE_NONE
;
315 sb
= blkid_probe_get_sb_buffer(pr
, mag
, sb_size
);
317 return BLKID_PROBE_NONE
;
318 sb_end
= sb
+ sb_size
;
320 if (!bcachefs_validate_checksum(pr
, bcs
, sb
, sb_end
))
321 return BLKID_PROBE_NONE
;
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
);
331 probe_bcachefs_sb_fields(pr
, bcs
, sb
, sb_end
);
333 return BLKID_PROBE_OK
;
336 const struct blkid_idinfo bcache_idinfo
=
339 .usage
= BLKID_USAGE_OTHER
,
340 .probefunc
= probe_bcache
,
345 .magic
= BCACHE_SB_MAGIC
,
346 .len
= BCACHE_SB_MAGIC_LEN
,
347 .kboff
= BCACHE_SB_KBOFF
,
348 .sboff
= BCACHE_SB_MAGIC_OFF
354 const struct blkid_idinfo bcachefs_idinfo
=
357 .usage
= BLKID_USAGE_FILESYSTEM
,
358 .probefunc
= probe_bcachefs
,
359 .minsz
= 256 * BCACHEFS_SECTOR_SIZE
,
362 .magic
= BCACHE_SB_MAGIC
,
363 .len
= BCACHE_SB_MAGIC_LEN
,
364 .kboff
= BCACHE_SB_KBOFF
,
365 .sboff
= BCACHE_SB_MAGIC_OFF
,
368 .magic
= BCACHEFS_SB_MAGIC
,
369 .len
= BCACHE_SB_MAGIC_LEN
,
370 .kboff
= BCACHE_SB_KBOFF
,
371 .sboff
= BCACHE_SB_MAGIC_OFF
,