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