Coverage Report

Created: 2023-06-07 06:09

/src/util-linux/libblkid/src/superblocks/hfs.c
Line
Count
Source (jump to first uncovered line)
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
77
#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
90
#define HFS_NODE_LEAF     0xff
62
134
#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
375
#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
232
#define HFSPLUS_SECTOR_SIZE        512
132
133
static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
134
258
{
135
258
  static unsigned char const hash_init[UL_MD5LENGTH] = {
136
258
    0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
137
258
    0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
138
258
  };
139
258
  unsigned char uuid[UL_MD5LENGTH];
140
258
  struct UL_MD5Context md5c;
141
142
258
  if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
143
33
    return -1;
144
145
225
  ul_MD5Init(&md5c);
146
225
  ul_MD5Update(&md5c, hash_init, UL_MD5LENGTH);
147
225
  ul_MD5Update(&md5c, hfs_info, len);
148
225
  ul_MD5Final(uuid, &md5c);
149
150
225
  uuid[6] = 0x30 | (uuid[6] & 0x0f);
151
225
  uuid[8] = 0x80 | (uuid[8] & 0x3f);
152
225
  return blkid_probe_set_uuid(pr, uuid);
153
258
}
154
155
static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
156
141
{
157
141
  struct hfs_mdb  *hfs;
158
141
  int size;
159
160
141
  hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
161
141
  if (!hfs)
162
0
    return errno ? -errno : 1;
163
164
141
  if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
165
141
      (memcmp(hfs->embed_sig, "HX", 2) == 0))
166
51
    return 1;  /* Not hfs, but an embedded HFS+ */
167
168
90
  size = be32_to_cpu(hfs->al_blk_size);
169
90
  if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
170
64
    DBG(LOWPROBE, ul_debug("\tbad allocation size - ignore"));
171
64
    return 1;
172
64
  }
173
174
26
  hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
175
176
26
  size = hfs->label_len;
177
26
  if ((size_t) size > sizeof(hfs->label))
178
11
    size = sizeof(hfs->label);
179
26
  blkid_probe_set_label(pr, hfs->label, size);
180
26
  return 0;
181
90
}
182
183
static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
184
373
{
185
373
  struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
186
373
  const struct hfsplus_bnode_descriptor *descr;
187
373
  const struct hfsplus_bheader_record *bnode;
188
373
  const struct hfsplus_catalog_key *key;
189
373
  const struct hfsplus_vol_header *hfsplus;
190
373
  const struct hfs_mdb *sbd;
191
373
  unsigned int alloc_block_size;
192
373
  unsigned int alloc_first_block;
193
373
  unsigned int embed_first_block;
194
373
  unsigned int off = 0;
195
373
  unsigned int blocksize;
196
373
  unsigned int cat_block;
197
373
  unsigned int ext_block_start = 0;
198
373
  unsigned int ext_block_count;
199
373
  unsigned int record_count;
200
373
  unsigned int leaf_node_head;
201
373
  unsigned int leaf_node_count;
202
373
  unsigned int leaf_node_size;
203
373
  unsigned int leaf_block;
204
373
  int ext;
205
373
  uint64_t leaf_off;
206
373
  const unsigned char *buf;
207
208
373
  sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
209
373
  if (!sbd)
210
0
    return errno ? -errno : 1;
211
212
  /* Check for a HFS+ volume embedded in a HFS volume */
213
373
  if (memcmp(sbd->signature, "BD", 2) == 0) {
214
141
    if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
215
141
        (memcmp(sbd->embed_sig, "HX", 2) != 0))
216
      /* This must be an HFS volume, so fail */
217
90
      return 1;
218
219
51
    alloc_block_size = be32_to_cpu(sbd->al_blk_size);
220
51
    alloc_first_block = be16_to_cpu(sbd->al_bl_st);
221
51
    embed_first_block = be16_to_cpu(sbd->embed_startblock);
222
51
    off = (alloc_first_block * 512) +
223
51
      (embed_first_block * alloc_block_size);
224
225
51
    buf = blkid_probe_get_buffer(pr,
226
51
        off + (mag->kboff * 1024),
227
51
        sizeof(struct hfsplus_vol_header));
228
51
    hfsplus = (const struct hfsplus_vol_header *) buf;
229
230
51
  } else
231
232
    hfsplus = blkid_probe_get_sb(pr, mag,
232
373
        struct hfsplus_vol_header);
233
234
283
  if (!hfsplus)
235
4
    return errno ? -errno : 1;
236
237
279
  if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
238
279
      (memcmp(hfsplus->signature, "HX", 2) != 0))
239
47
    return 1;
240
241
232
  hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
242
243
232
  blocksize = be32_to_cpu(hfsplus->blocksize);
244
232
  if (blocksize < HFSPLUS_SECTOR_SIZE)
245
27
    return 1;
246
247
205
  blkid_probe_set_fsblocksize(pr, blocksize);
248
205
  blkid_probe_set_block_size(pr, blocksize);
249
250
205
  memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
251
205
  cat_block = be32_to_cpu(extents[0].start_block);
252
253
205
  buf = blkid_probe_get_buffer(pr,
254
205
      off + ((uint64_t) cat_block * blocksize), 0x2000);
255
205
  if (!buf)
256
39
    return errno ? -errno : 0;
257
258
166
  bnode = (struct hfsplus_bheader_record *)
259
166
    &buf[sizeof(struct hfsplus_bnode_descriptor)];
260
261
166
  leaf_node_head = be32_to_cpu(bnode->leaf_head);
262
166
  leaf_node_size = be16_to_cpu(bnode->node_size);
263
166
  leaf_node_count = be32_to_cpu(bnode->leaf_count);
264
265
166
  if (leaf_node_size < sizeof(struct hfsplus_bnode_descriptor) +
266
166
      sizeof(struct hfsplus_catalog_key) || leaf_node_count == 0)
267
13
    return 0;
268
269
153
  leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
270
271
  /* get physical location */
272
235
  for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
273
234
    ext_block_start = be32_to_cpu(extents[ext].start_block);
274
234
    ext_block_count = be32_to_cpu(extents[ext].block_count);
275
234
    if (ext_block_count == 0)
276
13
      return 0;
277
278
    /* this is our extent */
279
221
    if (leaf_block < ext_block_count)
280
139
      break;
281
282
82
    leaf_block -= ext_block_count;
283
82
  }
284
140
  if (ext == HFSPLUS_EXTENT_COUNT)
285
1
    return 0;
286
287
139
  leaf_off = ((uint64_t) ext_block_start + leaf_block) * blocksize;
288
289
139
  buf = blkid_probe_get_buffer(pr,
290
139
        (uint64_t) off + leaf_off,
291
139
        leaf_node_size);
292
139
  if (!buf)
293
42
    return errno ? -errno : 0;
294
295
97
  descr = (struct hfsplus_bnode_descriptor *) buf;
296
97
  record_count = be16_to_cpu(descr->num_recs);
297
97
  if (record_count == 0)
298
7
    return 0;
299
300
90
  if (descr->type != HFS_NODE_LEAF)
301
23
    return 0;
302
303
67
  key = (struct hfsplus_catalog_key *)
304
67
    &buf[sizeof(struct hfsplus_bnode_descriptor)];
305
306
67
  if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID ||
307
67
      be16_to_cpu(key->unicode_len) > 255)
308
54
    return 0;
309
310
13
  blkid_probe_set_utf8label(pr, key->unicode,
311
13
      be16_to_cpu(key->unicode_len) * 2,
312
13
      UL_ENCODE_UTF16BE);
313
13
  return 0;
314
67
}
315
316
const struct blkid_idinfo hfs_idinfo =
317
{
318
  .name   = "hfs",
319
  .usage    = BLKID_USAGE_FILESYSTEM,
320
  .probefunc  = probe_hfs,
321
  .flags    = BLKID_IDINFO_TOLERANT,
322
  .magics   =
323
  {
324
    { .magic = "BD", .len = 2, .kboff = 1 },
325
    { NULL }
326
  }
327
};
328
329
const struct blkid_idinfo hfsplus_idinfo =
330
{
331
  .name   = "hfsplus",
332
  .usage    = BLKID_USAGE_FILESYSTEM,
333
  .probefunc  = probe_hfsplus,
334
  .magics   =
335
  {
336
    { .magic = "BD", .len = 2, .kboff = 1 },
337
    { .magic = "H+", .len = 2, .kboff = 1 },
338
    { .magic = "HX", .len = 2, .kboff = 1 },
339
    { NULL }
340
  }
341
};