bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 1 | /* |
| 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 | |
| 41 | typedef struct block_run { |
| 42 | int32_t allocation_group; |
| 43 | uint16_t start; |
| 44 | uint16_t len; |
| 45 | } __attribute__((packed)) block_run, inode_addr; |
| 46 | |
| 47 | struct 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 | |
| 70 | typedef 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 | |
| 80 | struct 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 | |
| 99 | struct small_data { |
| 100 | uint32_t type; |
| 101 | uint16_t name_size; |
| 102 | uint16_t data_size; |
| 103 | char name[0]; |
| 104 | } __attribute__((packed)); |
| 105 | |
| 106 | struct 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 | |
| 116 | struct 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 | |
| 125 | static 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 | |
| 138 | static 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 | |
| 157 | static 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 | |
| 233 | static 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 | |
| 254 | static 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 | |
| 331 | static 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 | |
| 416 | static 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 | |
| 465 | const 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 | }; |