Coverage Report

Created: 2026-03-03 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/superblocks/hfs.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
3
 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
4
 *
5
 * This file may be redistributed under the terms of the
6
 * GNU Lesser General Public License.
7
 */
8
#include <stdio.h>
9
#include <stdlib.h>
10
#include <unistd.h>
11
#include <string.h>
12
#include <inttypes.h>
13
14
#include "superblocks.h"
15
#include "md5.h"
16
17
/* HFS / HFS+ */
18
struct hfs_finder_info {
19
        uint32_t        boot_folder;
20
        uint32_t        start_app;
21
        uint32_t        open_folder;
22
        uint32_t        os9_folder;
23
        uint32_t        reserved;
24
        uint32_t        osx_folder;
25
        uint8_t         id[8];
26
} __attribute__((packed));
27
28
340
#define HFS_SECTOR_SIZE         512
29
30
struct hfs_mdb {
31
        uint8_t         signature[2];
32
        uint32_t        cr_date;
33
        uint32_t        ls_Mod;
34
        uint16_t        atrb;
35
        uint16_t        nm_fls;
36
        uint16_t        vbm_st;
37
        uint16_t        alloc_ptr;
38
        uint16_t        nm_al_blks;
39
        uint32_t        al_blk_size;
40
        uint32_t        clp_size;
41
        uint16_t        al_bl_st;
42
        uint32_t        nxt_cnid;
43
        uint16_t        free_bks;
44
        uint8_t         label_len;
45
        uint8_t         label[27];
46
        uint32_t        vol_bkup;
47
        uint16_t        vol_seq_num;
48
        uint32_t        wr_cnt;
49
        uint32_t        xt_clump_size;
50
        uint32_t        ct_clump_size;
51
        uint16_t        num_root_dirs;
52
        uint32_t        file_count;
53
        uint32_t        dir_count;
54
        struct hfs_finder_info finder_info;
55
        uint8_t         embed_sig[2];
56
        uint16_t        embed_startblock;
57
        uint16_t        embed_blockcount;
58
} __attribute__((packed));
59
60
61
28
#define HFS_NODE_LEAF     0xff
62
38
#define HFSPLUS_POR_CNID    1
63
64
struct hfsplus_bnode_descriptor {
65
  uint32_t    next;
66
  uint32_t    prev;
67
  uint8_t   type;
68
  uint8_t   height;
69
  uint16_t    num_recs;
70
  uint16_t    reserved;
71
} __attribute__((packed));
72
73
struct hfsplus_bheader_record {
74
  uint16_t    depth;
75
  uint32_t    root;
76
  uint32_t    leaf_count;
77
  uint32_t    leaf_head;
78
  uint32_t    leaf_tail;
79
  uint16_t    node_size;
80
} __attribute__((packed));
81
82
struct hfsplus_catalog_key {
83
  uint16_t  key_len;
84
  uint32_t  parent_id;
85
  uint16_t  unicode_len;
86
  uint8_t   unicode[255 * 2];
87
} __attribute__((packed));
88
89
struct hfsplus_extent {
90
  uint32_t    start_block;
91
  uint32_t    block_count;
92
} __attribute__((packed));
93
94
441
#define HFSPLUS_EXTENT_COUNT    8
95
struct hfsplus_fork {
96
  uint64_t    total_size;
97
  uint32_t    clump_size;
98
  uint32_t    total_blocks;
99
  struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
100
} __attribute__((packed));
101
102
struct hfsplus_vol_header {
103
  uint8_t   signature[2];
104
  uint16_t    version;
105
  uint32_t    attributes;
106
  uint32_t    last_mount_vers;
107
  uint32_t    reserved;
108
  uint32_t    create_date;
109
  uint32_t    modify_date;
110
  uint32_t    backup_date;
111
  uint32_t    checked_date;
112
  uint32_t    file_count;
113
  uint32_t    folder_count;
114
  uint32_t    blocksize;
115
  uint32_t    total_blocks;
116
  uint32_t    free_blocks;
117
  uint32_t    next_alloc;
118
  uint32_t    rsrc_clump_sz;
119
  uint32_t    data_clump_sz;
120
  uint32_t    next_cnid;
121
  uint32_t    write_count;
122
  uint64_t    encodings_bmp;
123
  struct hfs_finder_info finder_info;
124
  struct hfsplus_fork alloc_file;
125
  struct hfsplus_fork ext_file;
126
  struct hfsplus_fork cat_file;
127
  struct hfsplus_fork attr_file;
128
  struct hfsplus_fork start_file;
129
}  __attribute__((packed));
130
131
1.15k
#define HFSPLUS_SECTOR_SIZE        512
132
133
static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
134
131
{
135
131
  static unsigned char const hash_init[UL_MD5LENGTH] = {
136
131
    0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
137
131
    0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
138
131
  };
139
131
  unsigned char uuid[UL_MD5LENGTH];
140
131
  struct UL_MD5Context md5c;
141
142
131
  if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
143
39
    return -1;
144
145
92
  ul_MD5Init(&md5c);
146
92
  ul_MD5Update(&md5c, hash_init, UL_MD5LENGTH);
147
92
  ul_MD5Update(&md5c, hfs_info, len);
148
92
  ul_MD5Final(uuid, &md5c);
149
150
92
  uuid[6] = 0x30 | (uuid[6] & 0x0f);
151
92
  uuid[8] = 0x80 | (uuid[8] & 0x3f);
152
92
  return blkid_probe_set_uuid(pr, uuid);
153
131
}
154
155
static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
156
512
{
157
512
  const struct hfs_mdb  *hfs;
158
512
  int size;
159
160
512
  hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
161
512
  if (!hfs)
162
0
    return errno ? -errno : 1;
163
164
512
  if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
165
499
      (memcmp(hfs->embed_sig, "HX", 2) == 0))
166
136
    return 1;  /* Not hfs, but an embedded HFS+ */
167
168
376
  size = be32_to_cpu(hfs->al_blk_size);
169
376
  if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
170
357
    DBG(LOWPROBE, ul_debug("\tbad allocation size - ignore"));
171
357
    return 1;
172
357
  }
173
174
19
  hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
175
176
19
  size = hfs->label_len;
177
19
  if ((size_t) size > sizeof(hfs->label))
178
8
    size = sizeof(hfs->label);
179
19
  blkid_probe_set_label(pr, hfs->label, size);
180
19
  return 0;
181
376
}
182
183
static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
184
899
{
185
899
  struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
186
899
  const struct hfsplus_bnode_descriptor *descr;
187
899
  const struct hfsplus_bheader_record *bnode;
188
899
  const struct hfsplus_catalog_key *key;
189
899
  const struct hfsplus_vol_header *hfsplus;
190
899
  const struct hfs_mdb *sbd;
191
899
  unsigned int alloc_block_size;
192
899
  unsigned int alloc_first_block;
193
899
  unsigned int embed_first_block;
194
899
  uint64_t off = 0;
195
899
  unsigned int blocksize;
196
899
  unsigned int cat_block;
197
899
  unsigned int ext_block_start = 0;
198
899
  unsigned int ext_block_count;
199
899
  unsigned int record_count;
200
899
  unsigned int leaf_node_head;
201
899
  unsigned int leaf_node_count;
202
899
  unsigned int leaf_node_size;
203
899
  uint64_t leaf_block;
204
899
  int ext;
205
899
  uint64_t leaf_off;
206
899
  const unsigned char *buf;
207
208
899
  sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
209
899
  if (!sbd)
210
0
    return errno ? -errno : 1;
211
212
  /* Check for a HFS+ volume embedded in a HFS volume */
213
899
  if (memcmp(sbd->signature, "BD", 2) == 0) {
214
512
    if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
215
499
        (memcmp(sbd->embed_sig, "HX", 2) != 0))
216
      /* This must be an HFS volume, so fail */
217
376
      return 1;
218
219
136
    alloc_block_size = be32_to_cpu(sbd->al_blk_size);
220
136
    if (alloc_block_size < HFSPLUS_SECTOR_SIZE ||
221
111
        alloc_block_size % HFSPLUS_SECTOR_SIZE)
222
69
        return 1;
223
224
67
    alloc_first_block = be16_to_cpu(sbd->al_bl_st);
225
67
    embed_first_block = be16_to_cpu(sbd->embed_startblock);
226
67
    off = ((uint64_t) alloc_first_block * 512) +
227
67
      ((uint64_t) embed_first_block * alloc_block_size);
228
229
67
    buf = blkid_probe_get_buffer(pr,
230
67
        off + (mag->kboff * 1024),
231
67
        sizeof(struct hfsplus_vol_header));
232
67
    hfsplus = (const struct hfsplus_vol_header *) buf;
233
234
67
  } else
235
387
    hfsplus = blkid_probe_get_sb(pr, mag,
236
899
        struct hfsplus_vol_header);
237
238
454
  if (!hfsplus)
239
20
    return errno ? -errno : 1;
240
241
434
  if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
242
374
      (memcmp(hfsplus->signature, "HX", 2) != 0))
243
47
    return 1;
244
245
  /* Verify blocksize is initialized */
246
387
  blocksize = be32_to_cpu(hfsplus->blocksize);
247
387
  if (blocksize < HFSPLUS_SECTOR_SIZE || !is_power_of_2(blocksize))
248
223
    return 1;
249
250
  /* Save extends (hfsplus buffer may be later overwritten) */
251
164
  memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
252
253
  /* Make sure start_block is properly initialized */
254
164
  cat_block = be32_to_cpu(extents[0].start_block);
255
164
  if (off + ((uint64_t) cat_block * blocksize) > pr->size)
256
52
    return 1;
257
258
112
  hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
259
260
112
  blkid_probe_set_fsblocksize(pr, blocksize);
261
112
  blkid_probe_set_block_size(pr, blocksize);
262
263
112
  buf = blkid_probe_get_buffer(pr,
264
112
      off + ((uint64_t) cat_block * blocksize), 0x2000);
265
112
  if (!buf)
266
1
    return errno ? -errno : 0;
267
268
111
  bnode = (struct hfsplus_bheader_record *)
269
111
    &buf[sizeof(struct hfsplus_bnode_descriptor)];
270
271
111
  leaf_node_head = be32_to_cpu(bnode->leaf_head);
272
111
  leaf_node_size = be16_to_cpu(bnode->node_size);
273
111
  leaf_node_count = be32_to_cpu(bnode->leaf_count);
274
275
111
  if (leaf_node_size < sizeof(struct hfsplus_bnode_descriptor) +
276
111
      sizeof(struct hfsplus_catalog_key) || leaf_node_count == 0)
277
11
    return 0;
278
279
100
  leaf_block = ((uint64_t) leaf_node_head * leaf_node_size) / blocksize;
280
281
  /* get physical location */
282
370
  for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
283
355
    ext_block_start = be32_to_cpu(extents[ext].start_block);
284
355
    ext_block_count = be32_to_cpu(extents[ext].block_count);
285
355
    if (ext_block_count == 0)
286
29
      return 0;
287
288
    /* this is our extent */
289
326
    if (leaf_block < ext_block_count)
290
56
      break;
291
292
270
    leaf_block -= ext_block_count;
293
270
  }
294
71
  if (ext == HFSPLUS_EXTENT_COUNT)
295
15
    return 0;
296
297
56
  leaf_off = ((uint64_t) ext_block_start + leaf_block) * blocksize;
298
299
56
  buf = blkid_probe_get_buffer(pr,
300
56
        (uint64_t) off + leaf_off,
301
56
        leaf_node_size);
302
56
  if (!buf)
303
21
    return errno ? -errno : 0;
304
305
35
  descr = (struct hfsplus_bnode_descriptor *) buf;
306
35
  record_count = be16_to_cpu(descr->num_recs);
307
35
  if (record_count == 0)
308
7
    return 0;
309
310
28
  if (descr->type != HFS_NODE_LEAF)
311
9
    return 0;
312
313
19
  key = (struct hfsplus_catalog_key *)
314
19
    &buf[sizeof(struct hfsplus_bnode_descriptor)];
315
316
19
  if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID ||
317
3
      be16_to_cpu(key->unicode_len) > 255)
318
18
    return 0;
319
320
1
  blkid_probe_set_utf8label(pr, key->unicode,
321
      be16_to_cpu(key->unicode_len) * 2,
322
1
      UL_ENCODE_UTF16BE);
323
1
  return 0;
324
19
}
325
326
const struct blkid_idinfo hfs_idinfo =
327
{
328
  .name   = "hfs",
329
  .usage    = BLKID_USAGE_FILESYSTEM,
330
  .probefunc  = probe_hfs,
331
  .flags    = BLKID_IDINFO_TOLERANT,
332
  .magics   =
333
  {
334
    { .magic = "BD", .len = 2, .kboff = 1 },
335
    { NULL }
336
  }
337
};
338
339
const struct blkid_idinfo hfsplus_idinfo =
340
{
341
  .name   = "hfsplus",
342
  .usage    = BLKID_USAGE_FILESYSTEM,
343
  .probefunc  = probe_hfsplus,
344
  .magics   =
345
  {
346
    { .magic = "BD", .len = 2, .kboff = 1 },
347
    { .magic = "H+", .len = 2, .kboff = 1 },
348
    { .magic = "HX", .len = 2, .kboff = 1 },
349
    { NULL }
350
  }
351
};