blob: 6d960e9e71f09d5cf8cfae4cad22992abfd95998 [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
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+ */
18struct 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
28struct hfs_mdb {
29 uint8_t signature[2];
30 uint32_t cr_date;
31 uint32_t ls_Mod;
32 uint16_t atrb;
33 uint16_t nm_fls;
34 uint16_t vbm_st;
35 uint16_t alloc_ptr;
36 uint16_t nm_al_blks;
37 uint32_t al_blk_size;
38 uint32_t clp_size;
39 uint16_t al_bl_st;
40 uint32_t nxt_cnid;
41 uint16_t free_bks;
42 uint8_t label_len;
43 uint8_t label[27];
44 uint32_t vol_bkup;
45 uint16_t vol_seq_num;
46 uint32_t wr_cnt;
47 uint32_t xt_clump_size;
48 uint32_t ct_clump_size;
49 uint16_t num_root_dirs;
50 uint32_t file_count;
51 uint32_t dir_count;
52 struct hfs_finder_info finder_info;
53 uint8_t embed_sig[2];
54 uint16_t embed_startblock;
55 uint16_t embed_blockcount;
56} __attribute__((packed));
57
58
59#define HFS_NODE_LEAF 0xff
60#define HFSPLUS_POR_CNID 1
61
62struct hfsplus_bnode_descriptor {
63 uint32_t next;
64 uint32_t prev;
65 uint8_t type;
66 uint8_t height;
67 uint16_t num_recs;
68 uint16_t reserved;
69} __attribute__((packed));
70
71struct hfsplus_bheader_record {
72 uint16_t depth;
73 uint32_t root;
74 uint32_t leaf_count;
75 uint32_t leaf_head;
76 uint32_t leaf_tail;
77 uint16_t node_size;
78} __attribute__((packed));
79
80struct hfsplus_catalog_key {
81 uint16_t key_len;
82 uint32_t parent_id;
83 uint16_t unicode_len;
84 uint8_t unicode[255 * 2];
85} __attribute__((packed));
86
87struct hfsplus_extent {
88 uint32_t start_block;
89 uint32_t block_count;
90} __attribute__((packed));
91
92#define HFSPLUS_EXTENT_COUNT 8
93struct hfsplus_fork {
94 uint64_t total_size;
95 uint32_t clump_size;
96 uint32_t total_blocks;
97 struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
98} __attribute__((packed));
99
100struct hfsplus_vol_header {
101 uint8_t signature[2];
102 uint16_t version;
103 uint32_t attributes;
104 uint32_t last_mount_vers;
105 uint32_t reserved;
106 uint32_t create_date;
107 uint32_t modify_date;
108 uint32_t backup_date;
109 uint32_t checked_date;
110 uint32_t file_count;
111 uint32_t folder_count;
112 uint32_t blocksize;
113 uint32_t total_blocks;
114 uint32_t free_blocks;
115 uint32_t next_alloc;
116 uint32_t rsrc_clump_sz;
117 uint32_t data_clump_sz;
118 uint32_t next_cnid;
119 uint32_t write_count;
120 uint64_t encodings_bmp;
121 struct hfs_finder_info finder_info;
122 struct hfsplus_fork alloc_file;
123 struct hfsplus_fork ext_file;
124 struct hfsplus_fork cat_file;
125 struct hfsplus_fork attr_file;
126 struct hfsplus_fork start_file;
127} __attribute__((packed));
128
129#define HFSPLUS_SECTOR_SIZE 512
130
131static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
132{
133 static unsigned char const hash_init[MD5LENGTH] = {
134 0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
135 0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
136 };
137 unsigned char uuid[MD5LENGTH];
138 struct MD5Context md5c;
139
140 if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
141 return -1;
142 MD5Init(&md5c);
143 MD5Update(&md5c, hash_init, MD5LENGTH);
144 MD5Update(&md5c, hfs_info, len);
145 MD5Final(uuid, &md5c);
146 uuid[6] = 0x30 | (uuid[6] & 0x0f);
147 uuid[8] = 0x80 | (uuid[8] & 0x3f);
148 return blkid_probe_set_uuid(pr, uuid);
149}
150
151static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
152{
153 struct hfs_mdb *hfs;
154
155 hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
156 if (!hfs)
157 return -1;
158
159 if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
160 (memcmp(hfs->embed_sig, "HX", 2) == 0))
161 return 1; /* Not hfs, but an embedded HFS+ */
162
163 hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
164
165 blkid_probe_set_label(pr, hfs->label, hfs->label_len);
166 return 0;
167}
168
169static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
170{
171 struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
172 struct hfsplus_bnode_descriptor *descr;
173 struct hfsplus_bheader_record *bnode;
174 struct hfsplus_catalog_key *key;
175 struct hfsplus_vol_header *hfsplus;
176 struct hfs_mdb *sbd;
177 unsigned int alloc_block_size;
178 unsigned int alloc_first_block;
179 unsigned int embed_first_block;
180 unsigned int off = 0;
181 unsigned int blocksize;
182 unsigned int cat_block;
183 unsigned int ext_block_start;
184 unsigned int ext_block_count;
185 unsigned int record_count;
186 unsigned int leaf_node_head;
187 unsigned int leaf_node_count;
188 unsigned int leaf_node_size;
189 unsigned int leaf_block;
190 int ext;
191 uint64_t leaf_off;
192 unsigned char *buf;
193
194 sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
195 if (!sbd)
196 return -1;
197
198 /* Check for a HFS+ volume embedded in a HFS volume */
199 if (memcmp(sbd->signature, "BD", 2) == 0) {
200 if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
201 (memcmp(sbd->embed_sig, "HX", 2) != 0))
202 /* This must be an HFS volume, so fail */
203 return 1;
204
205 alloc_block_size = be32_to_cpu(sbd->al_blk_size);
206 alloc_first_block = be16_to_cpu(sbd->al_bl_st);
207 embed_first_block = be16_to_cpu(sbd->embed_startblock);
208 off = (alloc_first_block * 512) +
209 (embed_first_block * alloc_block_size);
210
211 buf = blkid_probe_get_buffer(pr,
212 off + (mag->kboff * 1024),
213 sizeof(struct hfsplus_vol_header));
214 hfsplus = (struct hfsplus_vol_header *) buf;
215
216 } else
217 hfsplus = blkid_probe_get_sb(pr, mag,
218 struct hfsplus_vol_header);
219
220 if (!hfsplus)
221 return -1;
222
223 if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
224 (memcmp(hfsplus->signature, "HX", 2) != 0))
225 return 1;
226
227 hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
228
229 blocksize = be32_to_cpu(hfsplus->blocksize);
230 if (blocksize < HFSPLUS_SECTOR_SIZE)
231 return -1;
232
233 memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
234 cat_block = be32_to_cpu(extents[0].start_block);
235
236 buf = blkid_probe_get_buffer(pr,
237 off + ((blkid_loff_t) cat_block * blocksize), 0x2000);
238 if (!buf)
239 return 0;
240
241 bnode = (struct hfsplus_bheader_record *)
242 &buf[sizeof(struct hfsplus_bnode_descriptor)];
243
244 leaf_node_head = be32_to_cpu(bnode->leaf_head);
245 leaf_node_size = be16_to_cpu(bnode->node_size);
246 leaf_node_count = be32_to_cpu(bnode->leaf_count);
247 if (leaf_node_count == 0)
248 return 0;
249
250 leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
251
252 /* get physical location */
253 for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
254 ext_block_start = be32_to_cpu(extents[ext].start_block);
255 ext_block_count = be32_to_cpu(extents[ext].block_count);
256 if (ext_block_count == 0)
257 return 0;
258
259 /* this is our extent */
260 if (leaf_block < ext_block_count)
261 break;
262
263 leaf_block -= ext_block_count;
264 }
265 if (ext == HFSPLUS_EXTENT_COUNT)
266 return 0;
267
268 leaf_off = (ext_block_start + leaf_block) * blocksize;
269
270 buf = blkid_probe_get_buffer(pr,
271 (blkid_loff_t) off + leaf_off,
272 leaf_node_size);
273 if (!buf)
274 return 0;
275
276 descr = (struct hfsplus_bnode_descriptor *) buf;
277 record_count = be16_to_cpu(descr->num_recs);
278 if (record_count == 0)
279 return 0;
280
281 if (descr->type != HFS_NODE_LEAF)
282 return 0;
283
284 key = (struct hfsplus_catalog_key *)
285 &buf[sizeof(struct hfsplus_bnode_descriptor)];
286
287 if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
288 return 0;
289
290 blkid_probe_set_utf8label(pr, key->unicode,
291 be16_to_cpu(key->unicode_len) * 2,
292 BLKID_ENC_UTF16BE);
293 return 0;
294}
295
296const struct blkid_idinfo hfs_idinfo =
297{
298 .name = "hfs",
299 .usage = BLKID_USAGE_FILESYSTEM,
300 .probefunc = probe_hfs,
301 .flags = BLKID_IDINFO_TOLERANT,
302 .magics =
303 {
304 { .magic = "BD", .len = 2, .kboff = 1 },
305 { NULL }
306 }
307};
308
309const struct blkid_idinfo hfsplus_idinfo =
310{
311 .name = "hfsplus",
312 .usage = BLKID_USAGE_FILESYSTEM,
313 .probefunc = probe_hfsplus,
314 .magics =
315 {
316 { .magic = "BD", .len = 2, .kboff = 1 },
317 { .magic = "H+", .len = 2, .kboff = 1 },
318 { .magic = "HX", .len = 2, .kboff = 1 },
319 { NULL }
320 }
321};