Coverage Report

Created: 2025-11-09 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/superblocks/befs.c
Line
Count
Source
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
19
#include "superblocks.h"
20
21
0
#define B_OS_NAME_LENGTH  0x20
22
0
#define SUPER_BLOCK_MAGIC1  0x42465331  /* BFS1 */
23
0
#define SUPER_BLOCK_MAGIC2  0xdd121031
24
0
#define SUPER_BLOCK_MAGIC3  0x15b6830e
25
0
#define SUPER_BLOCK_FS_ENDIAN 0x42494745  /* BIGE */
26
0
#define INODE_MAGIC1    0x3bbe0ad9
27
0
#define BPLUSTREE_MAGIC   0x69f6c2e8
28
0
#define BPLUSTREE_NULL    -1LL
29
0
#define NUM_DIRECT_BLOCKS 12
30
0
#define B_UINT64_TYPE   0x554c4c47  /* ULLG */
31
0
#define KEY_NAME    "be:volume_id"
32
0
#define KEY_SIZE    8
33
34
0
#define FS16_TO_CPU(value, fs_is_le) (fs_is_le ? le16_to_cpu(value) \
35
0
              : be16_to_cpu(value))
36
0
#define FS32_TO_CPU(value, fs_is_le) (fs_is_le ? le32_to_cpu(value) \
37
0
              : be32_to_cpu(value))
38
0
#define FS64_TO_CPU(value, fs_is_le) (fs_is_le ? le64_to_cpu(value) \
39
0
              : 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 const unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs,
126
          const struct block_run *br, int fs_le)
127
0
{
128
0
  return blkid_probe_get_buffer(pr,
129
0
      ((uint64_t) FS32_TO_CPU(br->allocation_group, fs_le)
130
0
          << FS32_TO_CPU(bs->ag_shift, fs_le)
131
0
          << FS32_TO_CPU(bs->block_shift, fs_le))
132
0
        + ((uint64_t) FS16_TO_CPU(br->start, fs_le)
133
0
          << FS32_TO_CPU(bs->block_shift, fs_le)),
134
0
      (uint64_t) FS16_TO_CPU(br->len, fs_le)
135
0
          << FS32_TO_CPU(bs->block_shift, fs_le));
136
0
}
137
138
static const 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
0
{
143
0
  if (offset + length > (int64_t) FS16_TO_CPU(br->len, fs_le)
144
0
          << FS32_TO_CPU(bs->block_shift, fs_le))
145
0
    return NULL;
146
147
0
  return blkid_probe_get_buffer(pr,
148
0
      ((uint64_t) FS32_TO_CPU(br->allocation_group, fs_le)
149
0
          << FS32_TO_CPU(bs->ag_shift, fs_le)
150
0
          << FS32_TO_CPU(bs->block_shift, fs_le))
151
0
        + ((uint64_t) FS16_TO_CPU(br->start, fs_le)
152
0
          << FS32_TO_CPU(bs->block_shift, fs_le))
153
0
        + offset,
154
0
      length);
155
0
}
156
157
static const 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
0
{
161
0
  if (start < (int64_t) FS64_TO_CPU(ds->max_direct_range, fs_le)) {
162
0
    int64_t br_len;
163
0
    size_t i;
164
165
0
    for (i = 0; i < NUM_DIRECT_BLOCKS; i++) {
166
0
      br_len = (int64_t) FS16_TO_CPU(ds->direct[i].len, fs_le)
167
0
          << FS32_TO_CPU(bs->block_shift, fs_le);
168
0
      if (start < br_len)
169
0
        return get_custom_block_run(pr, bs,
170
0
              &ds->direct[i],
171
0
              start, length, fs_le);
172
0
      start -= br_len;
173
0
      if (start < 0)
174
0
        return NULL; /* Corrupt? */
175
0
    }
176
0
  } else if (start < (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le)) {
177
0
    struct block_run *br;
178
0
    int64_t max_br, br_len, i;
179
180
0
    start -= FS64_TO_CPU(ds->max_direct_range, fs_le);
181
0
    if (start < 0)
182
0
      return NULL; /* Corrupt? */
183
184
0
    max_br = ((int64_t) FS16_TO_CPU(ds->indirect.len, fs_le)
185
0
          << FS32_TO_CPU(bs->block_shift, fs_le))
186
0
        / sizeof(struct block_run);
187
188
0
    br = (struct block_run *) get_block_run(pr, bs, &ds->indirect,
189
0
                  fs_le);
190
0
    if (!br)
191
0
      return NULL;
192
193
0
    for (i = 0; i < max_br; i++) {
194
0
      br_len = (int64_t) FS16_TO_CPU(br[i].len, fs_le)
195
0
          << FS32_TO_CPU(bs->block_shift, fs_le);
196
0
      if (start < br_len)
197
0
        return get_custom_block_run(pr, bs, &br[i],
198
0
              start, length, fs_le);
199
0
      start -= br_len;
200
0
    }
201
0
  } else if (start < (int64_t) FS64_TO_CPU(ds->max_double_indirect_range, fs_le)) {
202
0
    struct block_run *br;
203
0
    int64_t max_br, di_br_size, br_per_di_br, di_index, i_index;
204
205
0
    start -= (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le);
206
0
    if (start < 0)
207
0
      return NULL; /* Corrupt? */
208
209
0
    di_br_size = (int64_t) FS16_TO_CPU(ds->double_indirect.len,
210
0
        fs_le) << FS32_TO_CPU(bs->block_shift, fs_le);
211
0
    if (di_br_size == 0)
212
0
      return NULL;
213
214
0
    br_per_di_br = di_br_size / sizeof(struct block_run);
215
0
    if (br_per_di_br == 0)
216
0
      return NULL;
217
218
0
    di_index = start / (br_per_di_br * di_br_size);
219
0
    i_index = (start % (br_per_di_br * di_br_size)) / di_br_size;
220
0
    start = (start % (br_per_di_br * di_br_size)) % di_br_size;
221
222
0
    if (di_index >= br_per_di_br)
223
0
      return NULL; /* Corrupt? */
224
225
0
    br = (struct block_run *) get_block_run(pr, bs,
226
0
            &ds->double_indirect, fs_le);
227
0
    if (!br)
228
0
      return NULL;
229
230
0
    max_br = ((int64_t)FS16_TO_CPU(br[di_index].len, fs_le)
231
0
        << FS32_TO_CPU(bs->block_shift, fs_le))
232
0
      / sizeof(struct block_run);
233
234
0
    if (i_index >= max_br)
235
0
      return NULL; /* Corrupt? */
236
237
0
    br = (struct block_run *) get_block_run(pr, bs, &br[di_index],
238
0
                  fs_le);
239
0
    if (!br)
240
0
      return NULL;
241
242
0
    return get_custom_block_run(pr, bs, &br[i_index], start, length,
243
0
                  fs_le);
244
0
  }
245
0
  return NULL;
246
0
}
247
248
0
#define BAD_KEYS -2
249
250
static int32_t compare_keys(const char keys1[], uint16_t keylengths1[],
251
          int32_t index, const char *key2,
252
          uint16_t keylength2, uint16_t all_key_length,
253
          int fs_le)
254
0
{
255
0
  const char *key1;
256
0
  uint16_t keylength1, keystart1;
257
0
  int32_t result;
258
259
0
  keystart1 = index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1], fs_le);
260
0
  keylength1 = FS16_TO_CPU(keylengths1[index], fs_le) - keystart1;
261
262
0
  if (keystart1 + keylength1 > all_key_length)
263
0
    return BAD_KEYS; /* Corrupt? */
264
265
0
  key1 = &keys1[keystart1];
266
267
0
  result = strncmp(key1, key2, min(keylength1, keylength2));
268
269
0
  if (result == 0)
270
0
    return keylength1 - keylength2;
271
272
0
  if (result < 0) /* Don't clash with BAD_KEYS */
273
0
    result = -1;
274
275
0
  return result;
276
0
}
277
278
static int64_t get_key_value(blkid_probe pr, const struct befs_super_block *bs,
279
      const struct befs_inode *bi, const char *key, int fs_le)
280
0
{
281
0
  struct bplustree_header *bh;
282
0
  struct bplustree_node *bn;
283
0
  uint16_t *keylengths;
284
0
  int64_t *values;
285
0
  int64_t node_pointer;
286
0
  uint32_t bn_size, all_key_count, all_key_length;
287
0
  uint32_t keylengths_offset, values_offset;
288
0
  int32_t first, last, mid, cmp;
289
0
  int loop_detect = 0;
290
291
0
  errno = 0;
292
0
  bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
293
0
          sizeof(struct bplustree_header), fs_le);
294
0
  if (!bh)
295
0
    return errno ? -errno : -ENOENT;
296
297
0
  if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
298
0
    return -ENOENT;
299
300
0
  node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
301
0
  bn_size = FS32_TO_CPU(bh->node_size, fs_le);
302
303
0
  if (bn_size < sizeof(struct bplustree_node))
304
0
    return -ENOENT; /* Corrupt? */
305
306
0
  do {
307
0
    errno = 0;
308
309
0
    bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
310
0
      node_pointer, bn_size, fs_le);
311
0
    if (!bn)
312
0
      return errno ? -errno : -ENOENT;
313
314
0
    all_key_count = FS16_TO_CPU(bn->all_key_count, fs_le);
315
0
    all_key_length = FS16_TO_CPU(bn->all_key_length, fs_le);
316
0
    keylengths_offset =
317
0
      (sizeof(struct bplustree_node) + all_key_length
318
0
       + sizeof(int64_t) - 1) & ~(sizeof(int64_t) - 1);
319
0
    values_offset = keylengths_offset +
320
0
      all_key_count * sizeof(uint16_t);
321
322
0
    if (values_offset + all_key_count * sizeof(uint64_t) > bn_size)
323
0
      return -ENOENT; /* Corrupt? */
324
325
0
    keylengths = (uint16_t *) ((uint8_t *) bn + keylengths_offset);
326
0
    values = (int64_t *) ((uint8_t *) bn + values_offset);
327
328
0
    first = 0;
329
0
    mid = 0;
330
0
    last = all_key_count - 1;
331
332
0
    cmp = compare_keys(bn->name, keylengths, last, key,
333
0
           strlen(key), all_key_length, fs_le);
334
0
    if (cmp == BAD_KEYS)
335
0
      return -ENOENT;
336
337
0
    if (cmp == 0) {
338
0
      if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
339
0
              == BPLUSTREE_NULL)
340
0
        return FS64_TO_CPU(values[last], fs_le);
341
342
0
      node_pointer = FS64_TO_CPU(values[last], fs_le);
343
0
    } else if (cmp < 0)
344
0
      node_pointer = FS64_TO_CPU(bn->overflow_link, fs_le);
345
0
    else {
346
0
      while (first <= last) {
347
0
        mid = (first + last) / 2;
348
349
0
        cmp = compare_keys(bn->name, keylengths, mid,
350
0
               key, strlen(key),
351
0
               all_key_length, fs_le);
352
0
        if (cmp == BAD_KEYS)
353
0
          return -ENOENT;
354
355
0
        if (cmp == 0) {
356
0
          if ((int64_t) FS64_TO_CPU(bn->overflow_link,
357
0
            fs_le) == BPLUSTREE_NULL)
358
0
            return FS64_TO_CPU(values[mid],
359
0
                  fs_le);
360
0
          break;
361
0
        }
362
363
0
        if (cmp < 0)
364
0
          first = mid + 1;
365
0
        else
366
0
          last = mid - 1;
367
0
      }
368
0
      if (cmp < 0)
369
0
        node_pointer = FS64_TO_CPU(values[mid + 1],
370
0
                  fs_le);
371
0
      else
372
0
        node_pointer = FS64_TO_CPU(values[mid], fs_le);
373
0
    }
374
0
  } while (++loop_detect < 100 &&
375
0
    (int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
376
0
            != BPLUSTREE_NULL);
377
0
  return 0;
378
0
}
379
380
static int get_uuid(blkid_probe pr, const struct befs_super_block *bs,
381
          uint64_t * const uuid, int fs_le)
382
0
{
383
0
  struct befs_inode *bi;
384
0
  struct small_data *sd;
385
0
  uint64_t bi_size, offset, sd_size, sd_total_size;
386
387
0
  bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le);
388
0
  if (!bi)
389
0
    return errno ? -errno : BLKID_PROBE_NONE;
390
391
0
  if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
392
0
    return BLKID_PROBE_NONE;
393
394
0
  bi_size = (uint64_t)FS16_TO_CPU(bs->root_dir.len, fs_le) <<
395
0
    FS32_TO_CPU(bs->block_shift, fs_le);
396
0
  sd_total_size = min(bi_size - sizeof(struct befs_inode),
397
0
          (uint64_t)FS32_TO_CPU(bi->inode_size, fs_le));
398
399
0
  offset = 0;
400
401
0
  while (offset + sizeof(struct small_data) <= sd_total_size) {
402
0
    sd = (struct small_data *) ((uint8_t *)bi->small_data + offset);
403
0
    sd_size = sizeof(struct small_data)
404
0
      + FS16_TO_CPU(sd->name_size, fs_le) + 3
405
0
      + FS16_TO_CPU(sd->data_size, fs_le) + 1;
406
407
0
    if (offset + sd_size > sd_total_size)
408
0
      break;
409
410
0
    if (FS32_TO_CPU(sd->type, fs_le) == B_UINT64_TYPE
411
0
      && FS16_TO_CPU(sd->name_size, fs_le) == strlen(KEY_NAME)
412
0
      && FS16_TO_CPU(sd->data_size, fs_le) == KEY_SIZE
413
0
      && strcmp(sd->name, KEY_NAME) == 0) {
414
415
0
      memcpy(uuid,
416
0
             sd->name + FS16_TO_CPU(sd->name_size, fs_le) + 3,
417
0
             sizeof(uint64_t));
418
419
0
      break;
420
0
    }
421
422
0
    if (FS32_TO_CPU(sd->type, fs_le) == 0
423
0
        && FS16_TO_CPU(sd->name_size, fs_le) == 0
424
0
        && FS16_TO_CPU(sd->data_size, fs_le) == 0)
425
0
      break;
426
427
0
    offset += sd_size;
428
0
  }
429
430
0
  if (*uuid == 0
431
0
    && (FS32_TO_CPU(bi->attributes.allocation_group, fs_le) != 0
432
0
      || FS16_TO_CPU(bi->attributes.start, fs_le) != 0
433
0
      || FS16_TO_CPU(bi->attributes.len, fs_le) != 0)) {
434
0
    int64_t value;
435
436
0
    bi = (struct befs_inode *) get_block_run(pr, bs,
437
0
              &bi->attributes, fs_le);
438
0
    if (!bi)
439
0
      return errno ? -errno : BLKID_PROBE_NONE;
440
441
0
    if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
442
0
      return BLKID_PROBE_NONE;
443
444
0
    value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
445
0
    if (value < 0)
446
0
      return value == -ENOENT ? BLKID_PROBE_NONE : value;
447
448
0
    if (value > 0) {
449
0
      bi = (struct befs_inode *) blkid_probe_get_buffer(pr,
450
0
        value << FS32_TO_CPU(bs->block_shift, fs_le),
451
0
        FS32_TO_CPU(bs->block_size, fs_le));
452
0
      if (!bi)
453
0
        return errno ? -errno : BLKID_PROBE_NONE;
454
455
0
      if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
456
0
        return 1;
457
458
0
      if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE
459
0
        && FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE
460
0
        && FS16_TO_CPU(bi->data.direct[0].len, fs_le)
461
0
                  == 1) {
462
0
        uint64_t *attr_data;
463
464
0
        attr_data = (uint64_t *) get_block_run(pr, bs,
465
0
            &bi->data.direct[0], fs_le);
466
0
        if (!attr_data)
467
0
          return errno ? -errno : BLKID_PROBE_NONE;
468
469
0
        *uuid = *attr_data;
470
0
      }
471
0
    }
472
0
  }
473
0
  return 0;
474
0
}
475
476
static int probe_befs(blkid_probe pr, const struct blkid_idmag *mag)
477
0
{
478
0
  struct befs_super_block *bs;
479
0
  const char *version = NULL;
480
0
  uint64_t volume_id = 0;
481
0
  uint32_t block_size, block_shift;
482
0
  int fs_le, ret;
483
484
0
  bs = (struct befs_super_block *) blkid_probe_get_buffer(pr,
485
0
          mag->sboff - B_OS_NAME_LENGTH,
486
0
          sizeof(struct befs_super_block));
487
0
  if (!bs)
488
0
    return errno ? -errno : BLKID_PROBE_NONE;
489
490
0
  if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
491
0
    && le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
492
0
    && le32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
493
0
    && le32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
494
0
    fs_le = 1;
495
0
    version = "little-endian";
496
0
  } else if (be32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
497
0
    && be32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
498
0
    && be32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
499
0
    && be32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
500
0
    fs_le = 0;
501
0
    version = "big-endian";
502
0
  } else
503
0
    return BLKID_PROBE_NONE;
504
505
0
  block_size = FS32_TO_CPU(bs->block_size, fs_le);
506
0
  block_shift = FS32_TO_CPU(bs->block_shift, fs_le);
507
508
0
  if (block_shift < 10 || block_shift > 13 ||
509
0
      block_size != 1U << block_shift)
510
0
    return BLKID_PROBE_NONE;
511
512
0
  if (FS32_TO_CPU(bs->ag_shift, fs_le) > 64)
513
0
    return BLKID_PROBE_NONE;
514
515
0
  ret = get_uuid(pr, bs, &volume_id, fs_le);
516
517
0
  if (ret != 0)
518
0
    return ret;
519
520
  /*
521
   * all checks pass, set LABEL, VERSION and UUID
522
   */
523
0
  if (*bs->name != '\0')
524
0
    blkid_probe_set_label(pr, (unsigned char *) bs->name,
525
0
              sizeof(bs->name));
526
0
  if (version)
527
0
    blkid_probe_set_version(pr, version);
528
529
0
  if (volume_id)
530
0
    blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id,
531
0
          sizeof(volume_id), "%016" PRIx64,
532
0
          FS64_TO_CPU(volume_id, fs_le));
533
534
0
  blkid_probe_set_fsblocksize(pr, block_size);
535
0
  blkid_probe_set_block_size(pr, block_size);
536
0
  blkid_probe_set_fsendianness(pr,
537
0
      fs_le ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG);
538
539
0
  return BLKID_PROBE_OK;
540
0
}
541
542
const struct blkid_idinfo befs_idinfo =
543
{
544
  .name   = "befs",
545
  .usage    = BLKID_USAGE_FILESYSTEM,
546
  .probefunc  = probe_befs,
547
  .minsz    = 1024 * 1440,
548
  .magics   = {
549
    { .magic = "BFS1", .len = 4, .sboff = B_OS_NAME_LENGTH },
550
    { .magic = "1SFB", .len = 4, .sboff = B_OS_NAME_LENGTH },
551
    { .magic = "BFS1", .len = 4, .sboff = 0x200 +
552
              B_OS_NAME_LENGTH },
553
    { .magic = "1SFB", .len = 4, .sboff = 0x200 +
554
              B_OS_NAME_LENGTH },
555
    { NULL }
556
  }
557
};