/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 | | }; |