2
* Copyright (C) 2010 Jeroen Oortwijn <oortwijn@gmail.com>
4
* Partly based on the Haiku BFS driver by
5
* Axel Dörfler <axeld@pinc-software.de>
7
* Also inspired by the Linux BeFS driver by
8
* Will Dyson <will_dyson@pobox.com>, et al.
10
* This file may be redistributed under the terms of the
11
* GNU Lesser General Public License.
19
#include "superblocks.h"
21
#define B_OS_NAME_LENGTH 0x20
22
#define SUPER_BLOCK_MAGIC1 0x42465331 /* BFS1 */
23
#define SUPER_BLOCK_MAGIC2 0xdd121031
24
#define SUPER_BLOCK_MAGIC3 0x15b6830e
25
#define SUPER_BLOCK_FS_ENDIAN 0x42494745 /* BIGE */
26
#define INODE_MAGIC1 0x3bbe0ad9
27
#define BPLUSTREE_MAGIC 0x69f6c2e8
28
#define BPLUSTREE_NULL -1LL
29
#define NUM_DIRECT_BLOCKS 12
30
#define B_UINT64_TYPE 0x554c4c47 /* ULLG */
31
#define KEY_NAME "be:volume_id"
34
#define FS16_TO_CPU(value, fs_is_le) (fs_is_le ? le16_to_cpu(value) \
36
#define FS32_TO_CPU(value, fs_is_le) (fs_is_le ? le32_to_cpu(value) \
38
#define FS64_TO_CPU(value, fs_is_le) (fs_is_le ? le64_to_cpu(value) \
41
typedef struct block_run {
42
int32_t allocation_group;
45
} __attribute__((packed)) block_run, inode_addr;
47
struct befs_super_block {
48
char name[B_OS_NAME_LENGTH];
50
int32_t fs_byte_order;
57
int32_t blocks_per_ag;
68
} __attribute__((packed));
70
typedef struct data_stream {
71
block_run direct[NUM_DIRECT_BLOCKS];
72
int64_t max_direct_range;
74
int64_t max_indirect_range;
75
block_run double_indirect;
76
int64_t max_double_indirect_range;
78
} __attribute__((packed)) data_stream;
88
int64_t last_modified_time;
90
inode_addr attributes;
96
int32_t small_data[0];
97
} __attribute__((packed));
104
} __attribute__((packed));
106
struct bplustree_header {
109
uint32_t max_number_of_levels;
111
int64_t root_node_pointer;
112
int64_t free_node_pointer;
113
int64_t maximum_size;
114
} __attribute__((packed));
116
struct bplustree_node {
119
int64_t overflow_link;
120
uint16_t all_key_count;
121
uint16_t all_key_length;
123
} __attribute__((packed));
125
unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs,
126
const struct block_run *br, int fs_le)
128
return blkid_probe_get_buffer(pr,
129
((blkid_loff_t) FS32_TO_CPU(br->allocation_group, fs_le)
130
<< FS32_TO_CPU(bs->ag_shift, fs_le)
131
<< FS32_TO_CPU(bs->block_shift, fs_le))
132
+ ((blkid_loff_t) FS16_TO_CPU(br->start, fs_le)
133
<< FS32_TO_CPU(bs->block_shift, fs_le)),
134
(blkid_loff_t) FS16_TO_CPU(br->len, fs_le)
135
<< FS32_TO_CPU(bs->block_shift, fs_le));
138
unsigned char *get_custom_block_run(blkid_probe pr,
139
const struct befs_super_block *bs,
140
const struct block_run *br,
141
int64_t offset, uint32_t length, int fs_le)
143
if (offset + length > (int64_t) FS16_TO_CPU(br->len, fs_le)
144
<< FS32_TO_CPU(bs->block_shift, fs_le))
147
return blkid_probe_get_buffer(pr,
148
((blkid_loff_t) FS32_TO_CPU(br->allocation_group, fs_le)
149
<< FS32_TO_CPU(bs->ag_shift, fs_le)
150
<< FS32_TO_CPU(bs->block_shift, fs_le))
151
+ ((blkid_loff_t) FS16_TO_CPU(br->start, fs_le)
152
<< FS32_TO_CPU(bs->block_shift, fs_le))
157
unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs,
158
const struct data_stream *ds,
159
int64_t start, uint32_t length, int fs_le)
161
if (start < FS64_TO_CPU(ds->max_direct_range, fs_le)) {
165
for (i = 0; i < NUM_DIRECT_BLOCKS; i++) {
166
br_len = (int64_t) FS16_TO_CPU(ds->direct[i].len, fs_le)
167
<< FS32_TO_CPU(bs->block_shift, fs_le);
169
return get_custom_block_run(pr, bs,
171
start, length, fs_le);
175
} else if (start < FS64_TO_CPU(ds->max_indirect_range, fs_le)) {
176
struct block_run *br;
177
int64_t max_br, br_len, i;
179
start -= FS64_TO_CPU(ds->max_direct_range, fs_le);
180
max_br = ((int64_t) FS16_TO_CPU(ds->indirect.len, fs_le)
181
<< FS32_TO_CPU(bs->block_shift, fs_le))
182
/ sizeof(struct block_run);
184
br = (struct block_run *) get_block_run(pr, bs, &ds->indirect,
189
for (i = 0; i < max_br; i++) {
190
br_len = (int64_t) FS16_TO_CPU(br[i].len, fs_le)
191
<< FS32_TO_CPU(bs->block_shift, fs_le);
193
return get_custom_block_run(pr, bs, &br[i],
194
start, length, fs_le);
198
} else if (start < FS64_TO_CPU(ds->max_double_indirect_range, fs_le)) {
199
struct block_run *br;
200
int64_t di_br_size, br_per_di_br, di_index, i_index;
202
start -= FS64_TO_CPU(ds->max_indirect_range, fs_le);
203
di_br_size = (int64_t) FS16_TO_CPU(ds->double_indirect.len,
204
fs_le) << FS32_TO_CPU(bs->block_shift, fs_le);
205
br_per_di_br = di_br_size / sizeof(struct block_run);
206
di_index = start / (br_per_di_br * di_br_size);
207
i_index = (start % (br_per_di_br * di_br_size)) / di_br_size;
208
start = (start % (br_per_di_br * di_br_size)) % di_br_size;
210
br = (struct block_run *) get_block_run(pr, bs,
211
&ds->double_indirect, fs_le);
215
br = (struct block_run *) get_block_run(pr, bs, &br[di_index],
220
return get_custom_block_run(pr, bs, &br[i_index], start, length,
226
int32_t compare_keys(const char keys1[], uint16_t keylengths1[], int32_t index,
227
const char *key2, uint16_t keylength2, int fs_le)
233
key1 = &keys1[index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
235
keylength1 = FS16_TO_CPU(keylengths1[index], fs_le)
236
- (index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
239
result = strncmp(key1, key2, min(keylength1, keylength2));
242
return keylength1 - keylength2;
247
int64_t get_key_value(blkid_probe pr, const struct befs_super_block *bs,
248
const struct befs_inode *bi, const char *key, int fs_le)
250
struct bplustree_header *bh;
251
struct bplustree_node *bn;
252
uint16_t *keylengths;
254
int64_t node_pointer;
255
int32_t first, last, mid, cmp;
257
bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
258
sizeof(struct bplustree_header), fs_le);
262
if (FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
265
node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
268
bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
269
node_pointer, FS32_TO_CPU(bh->node_size, fs_le), fs_le);
273
keylengths = (uint16_t *) ((uint8_t *) bn
274
+ ((sizeof(struct bplustree_node)
275
+ FS16_TO_CPU(bn->all_key_length, fs_le)
276
+ sizeof(int64_t) - 1)
277
& ~(sizeof(int64_t) - 1)));
278
values = (int64_t *) ((uint8_t *) keylengths
279
+ FS16_TO_CPU(bn->all_key_count, fs_le)
283
last = FS16_TO_CPU(bn->all_key_count, fs_le) - 1;
285
cmp = compare_keys(bn->name, keylengths, last, key, strlen(key),
288
if (FS64_TO_CPU(bn->overflow_link, fs_le)
290
return FS64_TO_CPU(values[last], fs_le);
292
node_pointer = FS64_TO_CPU(values[last], fs_le);
294
node_pointer = FS64_TO_CPU(bn->overflow_link, fs_le);
296
while (first <= last) {
297
mid = (first + last) / 2;
299
cmp = compare_keys(bn->name, keylengths, mid,
300
key, strlen(key), fs_le);
302
if (FS64_TO_CPU(bn->overflow_link,
303
fs_le) == BPLUSTREE_NULL)
304
return FS64_TO_CPU(values[mid],
314
node_pointer = FS64_TO_CPU(values[mid + 1],
317
node_pointer = FS64_TO_CPU(values[mid], fs_le);
319
} while (FS64_TO_CPU(bn->overflow_link, fs_le) != BPLUSTREE_NULL);
323
int get_uuid(blkid_probe pr, const struct befs_super_block *bs,
324
uint64_t * const uuid, int fs_le)
326
struct befs_inode *bi;
327
struct small_data *sd;
329
bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le);
333
if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
336
sd = (struct small_data *) bi->small_data;
339
if (FS32_TO_CPU(sd->type, fs_le) == B_UINT64_TYPE
340
&& FS16_TO_CPU(sd->name_size, fs_le) == strlen(KEY_NAME)
341
&& FS16_TO_CPU(sd->data_size, fs_le) == KEY_SIZE
342
&& strcmp(sd->name, KEY_NAME) == 0) {
343
*uuid = *(uint64_t *) ((uint8_t *) sd->name
344
+ FS16_TO_CPU(sd->name_size, fs_le)
347
} else if (FS32_TO_CPU(sd->type, fs_le) == 0
348
&& FS16_TO_CPU(sd->name_size, fs_le) == 0
349
&& FS16_TO_CPU(sd->data_size, fs_le) == 0)
352
sd = (struct small_data *) ((uint8_t *) sd
353
+ sizeof(struct small_data)
354
+ FS16_TO_CPU(sd->name_size, fs_le) + 3
355
+ FS16_TO_CPU(sd->data_size, fs_le) + 1);
357
} while ((intptr_t) sd < (intptr_t) bi
358
+ FS32_TO_CPU(bi->inode_size, fs_le)
359
- sizeof(struct small_data));
361
&& (FS32_TO_CPU(bi->attributes.allocation_group, fs_le) != 0
362
|| FS16_TO_CPU(bi->attributes.start, fs_le) != 0
363
|| FS16_TO_CPU(bi->attributes.len, fs_le) != 0)) {
366
bi = (struct befs_inode *) get_block_run(pr, bs,
367
&bi->attributes, fs_le);
371
if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
374
value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
378
else if (value > 0) {
379
bi = (struct befs_inode *) blkid_probe_get_buffer(pr,
380
value << FS32_TO_CPU(bs->block_shift, fs_le),
381
FS32_TO_CPU(bs->block_size, fs_le));
385
if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
388
if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE
389
&& FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE
390
&& FS16_TO_CPU(bi->data.direct[0].len, fs_le)
394
attr_data = (uint64_t *) get_block_run(pr, bs,
395
&bi->data.direct[0], fs_le);
406
static int probe_befs(blkid_probe pr, const struct blkid_idmag *mag)
408
struct befs_super_block *bs;
409
const char *version = NULL;
410
uint64_t volume_id = 0;
413
bs = (struct befs_super_block *) blkid_probe_get_buffer(pr,
414
mag->sboff - B_OS_NAME_LENGTH,
415
sizeof(struct befs_super_block));
419
if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
420
&& le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
421
&& le32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
422
&& le32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
424
version = "little-endian";
425
} else if (be32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
426
&& be32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
427
&& be32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
428
&& be32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
430
version = "big-endian";
434
ret = get_uuid(pr, bs, &volume_id, fs_le);
440
* all checks pass, set LABEL, VERSION and UUID
442
if (strlen(bs->name))
443
blkid_probe_set_label(pr, (unsigned char *) bs->name,
446
blkid_probe_set_version(pr, version);
449
blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id,
450
sizeof(volume_id), "%016" PRIx64,
451
FS64_TO_CPU(volume_id, fs_le));
455
const struct blkid_idinfo befs_idinfo =
458
.usage = BLKID_USAGE_FILESYSTEM,
459
.probefunc = probe_befs,
460
.minsz = 1024 * 1440,
462
{ .magic = "BFS1", .len = 4, .sboff = B_OS_NAME_LENGTH },
463
{ .magic = "1SFB", .len = 4, .sboff = B_OS_NAME_LENGTH },
464
{ .magic = "BFS1", .len = 4, .sboff = 0x200 +
466
{ .magic = "1SFB", .len = 4, .sboff = 0x200 +