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