blob: 847860ad5ad07e4b4aaf7e867ae04f259837583a [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>
18#include <byteswap.h>
19#include "bitops.h"
20#include "superblocks.h"
21
22#define B_OS_NAME_LENGTH 0x20
23#define SUPER_BLOCK_MAGIC1 0x42465331 /* BFS1 */
24#define SUPER_BLOCK_MAGIC2 0xdd121031
25#define SUPER_BLOCK_MAGIC3 0x15b6830e
26#define SUPER_BLOCK_FS_ENDIAN 0x42494745 /* BIGE */
27#define INODE_MAGIC1 0x3bbe0ad9
28#define BPLUSTREE_MAGIC 0x69f6c2e8
29#define BPLUSTREE_NULL -1LL
30#define NUM_DIRECT_BLOCKS 12
31#define B_UINT64_TYPE 0x554c4c47 /* ULLG */
32#define KEY_NAME "be:volume_id"
33#define KEY_SIZE 8
34#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
264 bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
265 sizeof(struct bplustree_header), fs_le);
266 if (!bh)
267 return -1;
268
269 if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
270 return -1;
271
272 node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
273
274 do {
275 bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
276 node_pointer, FS32_TO_CPU(bh->node_size, fs_le), fs_le);
277 if (!bn)
278 return -1;
279
280 keylengths = (uint16_t *) ((uint8_t *) bn
281 + ((sizeof(struct bplustree_node)
282 + FS16_TO_CPU(bn->all_key_length, fs_le)
283 + sizeof(int64_t) - 1)
284 & ~(sizeof(int64_t) - 1)));
285 values = (int64_t *) ((uint8_t *) keylengths
286 + FS16_TO_CPU(bn->all_key_count, fs_le)
287 * sizeof(uint16_t));
288 first = 0;
289 mid = 0;
290 last = FS16_TO_CPU(bn->all_key_count, fs_le) - 1;
291
292 cmp = compare_keys(bn->name, keylengths, last, key, strlen(key),
293 fs_le);
294 if (cmp == 0) {
295 if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
296 == BPLUSTREE_NULL)
297 return FS64_TO_CPU(values[last], fs_le);
298 else
299 node_pointer = FS64_TO_CPU(values[last], fs_le);
300 } else if (cmp < 0)
301 node_pointer = FS64_TO_CPU(bn->overflow_link, fs_le);
302 else {
303 while (first <= last) {
304 mid = (first + last) / 2;
305
306 cmp = compare_keys(bn->name, keylengths, mid,
307 key, strlen(key), fs_le);
308 if (cmp == 0) {
309 if ((int64_t) FS64_TO_CPU(bn->overflow_link,
310 fs_le) == BPLUSTREE_NULL)
311 return FS64_TO_CPU(values[mid],
312 fs_le);
313 else
314 break;
315 } else if (cmp < 0)
316 first = mid + 1;
317 else
318 last = mid - 1;
319 }
320 if (cmp < 0)
321 node_pointer = FS64_TO_CPU(values[mid + 1],
322 fs_le);
323 else
324 node_pointer = FS64_TO_CPU(values[mid], fs_le);
325 }
326 } while ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
327 != BPLUSTREE_NULL);
328 return 0;
329}
330
331static int get_uuid(blkid_probe pr, const struct befs_super_block *bs,
332 uint64_t * const uuid, int fs_le)
333{
334 struct befs_inode *bi;
335 struct small_data *sd;
336
337 bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le);
338 if (!bi)
339 return -1;
340
341 if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
342 return -1;
343
344 sd = (struct small_data *) bi->small_data;
345
346 do {
347 if (FS32_TO_CPU(sd->type, fs_le) == B_UINT64_TYPE
348 && FS16_TO_CPU(sd->name_size, fs_le) == strlen(KEY_NAME)
349 && FS16_TO_CPU(sd->data_size, fs_le) == KEY_SIZE
350 && strcmp(sd->name, KEY_NAME) == 0) {
351
352 memcpy(uuid,
353 sd->name + FS16_TO_CPU(sd->name_size, fs_le) + 3,
354 sizeof(uint64_t));
355
356 break;
357 } else if (FS32_TO_CPU(sd->type, fs_le) == 0
358 && FS16_TO_CPU(sd->name_size, fs_le) == 0
359 && FS16_TO_CPU(sd->data_size, fs_le) == 0)
360 break;
361
362 sd = (struct small_data *) ((uint8_t *) sd
363 + sizeof(struct small_data)
364 + FS16_TO_CPU(sd->name_size, fs_le) + 3
365 + FS16_TO_CPU(sd->data_size, fs_le) + 1);
366
367 } while ((intptr_t) sd < (intptr_t) bi
368 + (int32_t) FS32_TO_CPU(bi->inode_size, fs_le)
369 - (int32_t) sizeof(struct small_data));
370 if (*uuid == 0
371 && (FS32_TO_CPU(bi->attributes.allocation_group, fs_le) != 0
372 || FS16_TO_CPU(bi->attributes.start, fs_le) != 0
373 || FS16_TO_CPU(bi->attributes.len, fs_le) != 0)) {
374 int64_t value;
375
376 bi = (struct befs_inode *) get_block_run(pr, bs,
377 &bi->attributes, fs_le);
378 if (!bi)
379 return -1;
380
381 if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
382 return -1;
383
384 value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
385
386 if (value < 0)
387 return value;
388 else if (value > 0) {
389 bi = (struct befs_inode *) blkid_probe_get_buffer(pr,
390 value << FS32_TO_CPU(bs->block_shift, fs_le),
391 FS32_TO_CPU(bs->block_size, fs_le));
392 if (!bi)
393 return -1;
394
395 if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
396 return -1;
397
398 if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE
399 && FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE
400 && FS16_TO_CPU(bi->data.direct[0].len, fs_le)
401 == 1) {
402 uint64_t *attr_data;
403
404 attr_data = (uint64_t *) get_block_run(pr, bs,
405 &bi->data.direct[0], fs_le);
406 if (!attr_data)
407 return -1;
408
409 *uuid = *attr_data;
410 }
411 }
412 }
413 return 0;
414}
415
416static int probe_befs(blkid_probe pr, const struct blkid_idmag *mag)
417{
418 struct befs_super_block *bs;
419 const char *version = NULL;
420 uint64_t volume_id = 0;
421 int fs_le, ret;
422
423 bs = (struct befs_super_block *) blkid_probe_get_buffer(pr,
424 mag->sboff - B_OS_NAME_LENGTH,
425 sizeof(struct befs_super_block));
426 if (!bs)
427 return -1;
428
429 if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
430 && le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
431 && le32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
432 && le32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
433 fs_le = 1;
434 version = "little-endian";
435 } else if (be32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
436 && be32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
437 && be32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
438 && be32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
439 fs_le = 0;
440 version = "big-endian";
441 } else
442 return -1;
443
444 ret = get_uuid(pr, bs, &volume_id, fs_le);
445
446 if (ret < 0)
447 return ret;
448
449 /*
450 * all checks pass, set LABEL, VERSION and UUID
451 */
452 if (strlen(bs->name))
453 blkid_probe_set_label(pr, (unsigned char *) bs->name,
454 sizeof(bs->name));
455 if (version)
456 blkid_probe_set_version(pr, version);
457
458 if (volume_id)
459 blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id,
460 sizeof(volume_id), "%016" PRIx64,
461 FS64_TO_CPU(volume_id, fs_le));
462 return 0;
463}
464
465const struct blkid_idinfo befs_idinfo =
466{
467 .name = "befs",
468 .usage = BLKID_USAGE_FILESYSTEM,
469 .probefunc = probe_befs,
470 .minsz = 1024 * 1440,
471 .magics = {
472 { .magic = "BFS1", .len = 4, .sboff = B_OS_NAME_LENGTH },
473 { .magic = "1SFB", .len = 4, .sboff = B_OS_NAME_LENGTH },
474 { .magic = "BFS1", .len = 4, .sboff = 0x200 +
475 B_OS_NAME_LENGTH },
476 { .magic = "1SFB", .len = 4, .sboff = 0x200 +
477 B_OS_NAME_LENGTH },
478 { NULL }
479 }
480};