blob: e4453bc1779718a34bc7567cb577f60c32b54f63 [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
2 * Copyright (C) 2010 Jeroen Oortwijn <oortwijn@gmail.com>
3 *
4 * Partly based on the Haiku BFS driver by
5 * Axel Dörfler <axeld@pinc-software.de>
6 *
7 * Also inspired by the Linux BeFS driver by
8 * Will Dyson <will_dyson@pobox.com>, et al.
9 *
10 * This file may be redistributed under the terms of the
11 * GNU Lesser General Public License.
12 */
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <string.h>
17#include <inttypes.h>
bigbiff7b4c7a62015-01-01 19:44:14 -050018
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050019#include "superblocks.h"
20
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"
32#define KEY_SIZE 8
bigbiff7b4c7a62015-01-01 19:44:14 -050033
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050034#define FS16_TO_CPU(value, fs_is_le) (fs_is_le ? le16_to_cpu(value) \
35 : be16_to_cpu(value))
36#define FS32_TO_CPU(value, fs_is_le) (fs_is_le ? le32_to_cpu(value) \
37 : be32_to_cpu(value))
38#define FS64_TO_CPU(value, fs_is_le) (fs_is_le ? le64_to_cpu(value) \
39 : be64_to_cpu(value))
40
41typedef struct block_run {
42 int32_t allocation_group;
43 uint16_t start;
44 uint16_t len;
45} __attribute__((packed)) block_run, inode_addr;
46
47struct befs_super_block {
48 char name[B_OS_NAME_LENGTH];
49 int32_t magic1;
50 int32_t fs_byte_order;
51 uint32_t block_size;
52 uint32_t block_shift;
53 int64_t num_blocks;
54 int64_t used_blocks;
55 int32_t inode_size;
56 int32_t magic2;
57 int32_t blocks_per_ag;
58 int32_t ag_shift;
59 int32_t num_ags;
60 int32_t flags;
61 block_run log_blocks;
62 int64_t log_start;
63 int64_t log_end;
64 int32_t magic3;
65 inode_addr root_dir;
66 inode_addr indices;
67 int32_t pad[8];
68} __attribute__((packed));
69
70typedef struct data_stream {
71 block_run direct[NUM_DIRECT_BLOCKS];
72 int64_t max_direct_range;
73 block_run indirect;
74 int64_t max_indirect_range;
75 block_run double_indirect;
76 int64_t max_double_indirect_range;
77 int64_t size;
78} __attribute__((packed)) data_stream;
79
80struct befs_inode {
81 int32_t magic1;
82 inode_addr inode_num;
83 int32_t uid;
84 int32_t gid;
85 int32_t mode;
86 int32_t flags;
87 int64_t create_time;
88 int64_t last_modified_time;
89 inode_addr parent;
90 inode_addr attributes;
91 uint32_t type;
92 int32_t inode_size;
93 uint32_t etc;
94 data_stream data;
95 int32_t pad[4];
96 int32_t small_data[0];
97} __attribute__((packed));
98
99struct small_data {
100 uint32_t type;
101 uint16_t name_size;
102 uint16_t data_size;
103 char name[0];
104} __attribute__((packed));
105
106struct bplustree_header {
107 uint32_t magic;
108 uint32_t node_size;
109 uint32_t max_number_of_levels;
110 uint32_t data_type;
111 int64_t root_node_pointer;
112 int64_t free_node_pointer;
113 int64_t maximum_size;
114} __attribute__((packed));
115
116struct bplustree_node {
117 int64_t left_link;
118 int64_t right_link;
119 int64_t overflow_link;
120 uint16_t all_key_count;
121 uint16_t all_key_length;
122 char name[0];
123} __attribute__((packed));
124
125static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs,
126 const struct block_run *br, int fs_le)
127{
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));
136}
137
138static 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)
142{
143 if (offset + length > (int64_t) FS16_TO_CPU(br->len, fs_le)
144 << FS32_TO_CPU(bs->block_shift, fs_le))
145 return NULL;
146
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))
153 + offset,
154 length);
155}
156
157static 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)
160{
161 if (start < (int64_t) FS64_TO_CPU(ds->max_direct_range, fs_le)) {
162 int64_t br_len;
163 size_t i;
164
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);
168 if (start < br_len)
169 return get_custom_block_run(pr, bs,
170 &ds->direct[i],
171 start, length, fs_le);
172 else
173 start -= br_len;
174 }
175 } else if (start < (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le)) {
176 struct block_run *br;
177 int64_t max_br, br_len, i;
178
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);
183
184 br = (struct block_run *) get_block_run(pr, bs, &ds->indirect,
185 fs_le);
186 if (!br)
187 return NULL;
188
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);
192 if (start < br_len)
193 return get_custom_block_run(pr, bs, &br[i],
194 start, length, fs_le);
195 else
196 start -= br_len;
197 }
198 } else if (start < (int64_t) 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;
201
202 start -= (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le);
203
204 di_br_size = (int64_t) FS16_TO_CPU(ds->double_indirect.len,
205 fs_le) << FS32_TO_CPU(bs->block_shift, fs_le);
206 if (di_br_size == 0)
207 return NULL;
208
209 br_per_di_br = di_br_size / sizeof(struct block_run);
210 if (br_per_di_br == 0)
211 return NULL;
212
213 di_index = start / (br_per_di_br * di_br_size);
214 i_index = (start % (br_per_di_br * di_br_size)) / di_br_size;
215 start = (start % (br_per_di_br * di_br_size)) % di_br_size;
216
217 br = (struct block_run *) get_block_run(pr, bs,
218 &ds->double_indirect, fs_le);
219 if (!br)
220 return NULL;
221
222 br = (struct block_run *) get_block_run(pr, bs, &br[di_index],
223 fs_le);
224 if (!br)
225 return NULL;
226
227 return get_custom_block_run(pr, bs, &br[i_index], start, length,
228 fs_le);
229 }
230 return NULL;
231}
232
233static int32_t compare_keys(const char keys1[], uint16_t keylengths1[], int32_t index,
234 const char *key2, uint16_t keylength2, int fs_le)
235{
236 const char *key1;
237 uint16_t keylength1;
238 int32_t result;
239
240 key1 = &keys1[index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
241 fs_le)];
242 keylength1 = FS16_TO_CPU(keylengths1[index], fs_le)
243 - (index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
244 fs_le));
245
246 result = strncmp(key1, key2, min(keylength1, keylength2));
247
248 if (result == 0)
249 return keylength1 - keylength2;
250
251 return result;
252}
253
254static int64_t get_key_value(blkid_probe pr, const struct befs_super_block *bs,
255 const struct befs_inode *bi, const char *key, int fs_le)
256{
257 struct bplustree_header *bh;
258 struct bplustree_node *bn;
259 uint16_t *keylengths;
260 int64_t *values;
261 int64_t node_pointer;
262 int32_t first, last, mid, cmp;
263
bigbiff7b4c7a62015-01-01 19:44:14 -0500264 errno = 0;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500265 bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
266 sizeof(struct bplustree_header), fs_le);
267 if (!bh)
bigbiff7b4c7a62015-01-01 19:44:14 -0500268 return errno ? -errno : -ENOENT;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500269
270 if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
bigbiff7b4c7a62015-01-01 19:44:14 -0500271 return -ENOENT;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500272
273 node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
274
275 do {
bigbiff7b4c7a62015-01-01 19:44:14 -0500276 errno = 0;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500277 bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
278 node_pointer, FS32_TO_CPU(bh->node_size, fs_le), fs_le);
279 if (!bn)
bigbiff7b4c7a62015-01-01 19:44:14 -0500280 return errno ? -errno : -ENOENT;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500281
282 keylengths = (uint16_t *) ((uint8_t *) bn
283 + ((sizeof(struct bplustree_node)
284 + FS16_TO_CPU(bn->all_key_length, fs_le)
285 + sizeof(int64_t) - 1)
286 & ~(sizeof(int64_t) - 1)));
287 values = (int64_t *) ((uint8_t *) keylengths
288 + FS16_TO_CPU(bn->all_key_count, fs_le)
289 * sizeof(uint16_t));
290 first = 0;
291 mid = 0;
292 last = FS16_TO_CPU(bn->all_key_count, fs_le) - 1;
293
294 cmp = compare_keys(bn->name, keylengths, last, key, strlen(key),
295 fs_le);
296 if (cmp == 0) {
297 if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
298 == BPLUSTREE_NULL)
299 return FS64_TO_CPU(values[last], fs_le);
300 else
301 node_pointer = FS64_TO_CPU(values[last], fs_le);
302 } else if (cmp < 0)
303 node_pointer = FS64_TO_CPU(bn->overflow_link, fs_le);
304 else {
305 while (first <= last) {
306 mid = (first + last) / 2;
307
308 cmp = compare_keys(bn->name, keylengths, mid,
309 key, strlen(key), fs_le);
310 if (cmp == 0) {
311 if ((int64_t) FS64_TO_CPU(bn->overflow_link,
312 fs_le) == BPLUSTREE_NULL)
313 return FS64_TO_CPU(values[mid],
314 fs_le);
315 else
316 break;
317 } else if (cmp < 0)
318 first = mid + 1;
319 else
320 last = mid - 1;
321 }
322 if (cmp < 0)
323 node_pointer = FS64_TO_CPU(values[mid + 1],
324 fs_le);
325 else
326 node_pointer = FS64_TO_CPU(values[mid], fs_le);
327 }
328 } while ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
329 != BPLUSTREE_NULL);
330 return 0;
331}
332
333static int get_uuid(blkid_probe pr, const struct befs_super_block *bs,
334 uint64_t * const uuid, int fs_le)
335{
336 struct befs_inode *bi;
337 struct small_data *sd;
338
339 bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le);
340 if (!bi)
bigbiff7b4c7a62015-01-01 19:44:14 -0500341 return errno ? -errno : BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500342
343 if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
bigbiff7b4c7a62015-01-01 19:44:14 -0500344 return BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500345
346 sd = (struct small_data *) bi->small_data;
347
348 do {
349 if (FS32_TO_CPU(sd->type, fs_le) == B_UINT64_TYPE
350 && FS16_TO_CPU(sd->name_size, fs_le) == strlen(KEY_NAME)
351 && FS16_TO_CPU(sd->data_size, fs_le) == KEY_SIZE
352 && strcmp(sd->name, KEY_NAME) == 0) {
353
354 memcpy(uuid,
355 sd->name + FS16_TO_CPU(sd->name_size, fs_le) + 3,
356 sizeof(uint64_t));
357
358 break;
359 } else if (FS32_TO_CPU(sd->type, fs_le) == 0
360 && FS16_TO_CPU(sd->name_size, fs_le) == 0
361 && FS16_TO_CPU(sd->data_size, fs_le) == 0)
362 break;
363
364 sd = (struct small_data *) ((uint8_t *) sd
365 + sizeof(struct small_data)
366 + FS16_TO_CPU(sd->name_size, fs_le) + 3
367 + FS16_TO_CPU(sd->data_size, fs_le) + 1);
368
369 } while ((intptr_t) sd < (intptr_t) bi
370 + (int32_t) FS32_TO_CPU(bi->inode_size, fs_le)
371 - (int32_t) sizeof(struct small_data));
372 if (*uuid == 0
373 && (FS32_TO_CPU(bi->attributes.allocation_group, fs_le) != 0
374 || FS16_TO_CPU(bi->attributes.start, fs_le) != 0
375 || FS16_TO_CPU(bi->attributes.len, fs_le) != 0)) {
376 int64_t value;
377
378 bi = (struct befs_inode *) get_block_run(pr, bs,
379 &bi->attributes, fs_le);
380 if (!bi)
bigbiff7b4c7a62015-01-01 19:44:14 -0500381 return errno ? -errno : BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500382
383 if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
bigbiff7b4c7a62015-01-01 19:44:14 -0500384 return BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500385
386 value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500387 if (value < 0)
bigbiff7b4c7a62015-01-01 19:44:14 -0500388 return value == -ENOENT ? BLKID_PROBE_NONE : value;
389
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500390 else if (value > 0) {
391 bi = (struct befs_inode *) blkid_probe_get_buffer(pr,
392 value << FS32_TO_CPU(bs->block_shift, fs_le),
393 FS32_TO_CPU(bs->block_size, fs_le));
394 if (!bi)
bigbiff7b4c7a62015-01-01 19:44:14 -0500395 return errno ? -errno : BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500396
397 if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
bigbiff7b4c7a62015-01-01 19:44:14 -0500398 return 1;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500399
400 if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE
401 && FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE
402 && FS16_TO_CPU(bi->data.direct[0].len, fs_le)
403 == 1) {
404 uint64_t *attr_data;
405
406 attr_data = (uint64_t *) get_block_run(pr, bs,
407 &bi->data.direct[0], fs_le);
408 if (!attr_data)
bigbiff7b4c7a62015-01-01 19:44:14 -0500409 return errno ? -errno : BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500410
411 *uuid = *attr_data;
412 }
413 }
414 }
415 return 0;
416}
417
418static int probe_befs(blkid_probe pr, const struct blkid_idmag *mag)
419{
420 struct befs_super_block *bs;
421 const char *version = NULL;
422 uint64_t volume_id = 0;
423 int fs_le, ret;
424
425 bs = (struct befs_super_block *) blkid_probe_get_buffer(pr,
426 mag->sboff - B_OS_NAME_LENGTH,
427 sizeof(struct befs_super_block));
428 if (!bs)
bigbiff7b4c7a62015-01-01 19:44:14 -0500429 return errno ? -errno : BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500430
431 if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
432 && le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
433 && le32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
434 && le32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
435 fs_le = 1;
436 version = "little-endian";
437 } else if (be32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
438 && be32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
439 && be32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
440 && be32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
441 fs_le = 0;
442 version = "big-endian";
443 } else
bigbiff7b4c7a62015-01-01 19:44:14 -0500444 return BLKID_PROBE_NONE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500445
446 ret = get_uuid(pr, bs, &volume_id, fs_le);
447
bigbiff7b4c7a62015-01-01 19:44:14 -0500448 if (ret != 0)
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500449 return ret;
450
451 /*
452 * all checks pass, set LABEL, VERSION and UUID
453 */
454 if (strlen(bs->name))
455 blkid_probe_set_label(pr, (unsigned char *) bs->name,
456 sizeof(bs->name));
457 if (version)
458 blkid_probe_set_version(pr, version);
459
460 if (volume_id)
461 blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id,
462 sizeof(volume_id), "%016" PRIx64,
463 FS64_TO_CPU(volume_id, fs_le));
bigbiff7b4c7a62015-01-01 19:44:14 -0500464 return BLKID_PROBE_OK;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500465}
466
467const struct blkid_idinfo befs_idinfo =
468{
469 .name = "befs",
470 .usage = BLKID_USAGE_FILESYSTEM,
471 .probefunc = probe_befs,
472 .minsz = 1024 * 1440,
473 .magics = {
474 { .magic = "BFS1", .len = 4, .sboff = B_OS_NAME_LENGTH },
475 { .magic = "1SFB", .len = 4, .sboff = B_OS_NAME_LENGTH },
476 { .magic = "BFS1", .len = 4, .sboff = 0x200 +
477 B_OS_NAME_LENGTH },
478 { .magic = "1SFB", .len = 4, .sboff = 0x200 +
479 B_OS_NAME_LENGTH },
480 { NULL }
481 }
482};