chiark / gitweb /
905471cb357a2afae80bfdc1975121337e19a1e2
[elogind.git] / extras / volume_id / volume_id / hfs / hfs.c
1 /*
2  * volume_id - reads filesystem label and uuid
3  *
4  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  *      This library is free software; you can redistribute it and/or
7  *      modify it under the terms of the GNU Lesser General Public
8  *      License as published by the Free Software Foundation; either
9  *      version 2.1 of the License, or (at your option) any later version.
10  *
11  *      This library is distributed in the hope that it will be useful,
12  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *      Lesser General Public License for more details.
15  *
16  *      You should have received a copy of the GNU Lesser General Public
17  *      License along with this library; if not, write to the Free Software
18  *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE 1
23 #endif
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <asm/types.h>
36
37 #include "../volume_id.h"
38 #include "../logging.h"
39 #include "../util.h"
40 #include "hfs.h"
41
42 struct hfs_finder_info{
43         __u32   boot_folder;
44         __u32   start_app;
45         __u32   open_folder;
46         __u32   os9_folder;
47         __u32   reserved;
48         __u32   osx_folder;
49         __u8    id[8];
50 } __attribute__((__packed__));
51
52 struct hfs_mdb {
53         __u8    signature[2];
54         __u32   cr_date;
55         __u32   ls_Mod;
56         __u16   atrb;
57         __u16   nm_fls;
58         __u16   vbm_st;
59         __u16   alloc_ptr;
60         __u16   nm_al_blks;
61         __u32   al_blk_size;
62         __u32   clp_size;
63         __u16   al_bl_st;
64         __u32   nxt_cnid;
65         __u16   free_bks;
66         __u8    label_len;
67         __u8    label[27];
68         __u32   vol_bkup;
69         __u16   vol_seq_num;
70         __u32   wr_cnt;
71         __u32   xt_clump_size;
72         __u32   ct_clump_size;
73         __u16   num_root_dirs;
74         __u32   file_count;
75         __u32   dir_count;
76         struct hfs_finder_info finder_info;
77         __u8    embed_sig[2];
78         __u16   embed_startblock;
79         __u16   embed_blockcount;
80 } __attribute__((__packed__)) *hfs;
81
82 struct hfsplus_bnode_descriptor {
83         __u32   next;
84         __u32   prev;
85         __u8    type;
86         __u8    height;
87         __u16   num_recs;
88         __u16   reserved;
89 } __attribute__((__packed__));
90
91 struct hfsplus_bheader_record {
92         __u16   depth;
93         __u32   root;
94         __u32   leaf_count;
95         __u32   leaf_head;
96         __u32   leaf_tail;
97         __u16   node_size;
98 } __attribute__((__packed__));
99
100 struct hfsplus_catalog_key {
101         __u16   key_len;
102         __u32   parent_id;
103         __u16   unicode_len;
104         __u8    unicode[255 * 2];
105 } __attribute__((__packed__));
106
107 struct hfsplus_extent {
108         __u32 start_block;
109         __u32 block_count;
110 } __attribute__((__packed__));
111
112 #define HFSPLUS_EXTENT_COUNT            8
113 struct hfsplus_fork {
114         __u64 total_size;
115         __u32 clump_size;
116         __u32 total_blocks;
117         struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
118 } __attribute__((__packed__));
119
120 struct hfsplus_vol_header {
121         __u8    signature[2];
122         __u16   version;
123         __u32   attributes;
124         __u32   last_mount_vers;
125         __u32   reserved;
126         __u32   create_date;
127         __u32   modify_date;
128         __u32   backup_date;
129         __u32   checked_date;
130         __u32   file_count;
131         __u32   folder_count;
132         __u32   blocksize;
133         __u32   total_blocks;
134         __u32   free_blocks;
135         __u32   next_alloc;
136         __u32   rsrc_clump_sz;
137         __u32   data_clump_sz;
138         __u32   next_cnid;
139         __u32   write_count;
140         __u64   encodings_bmp;
141         struct hfs_finder_info finder_info;
142         struct hfsplus_fork alloc_file;
143         struct hfsplus_fork ext_file;
144         struct hfsplus_fork cat_file;
145         struct hfsplus_fork attr_file;
146         struct hfsplus_fork start_file;
147 } __attribute__((__packed__)) *hfsplus;
148
149 #define HFS_SUPERBLOCK_OFFSET           0x400
150 #define HFS_NODE_LEAF                   0xff
151 #define HFSPLUS_POR_CNID                1
152
153 int volume_id_probe_hfs_hfsplus(struct volume_id *id, __u64 off)
154 {
155         unsigned int blocksize;
156         unsigned int cat_block;
157         unsigned int ext_block_start;
158         unsigned int ext_block_count;
159         int ext;
160         unsigned int leaf_node_head;
161         unsigned int leaf_node_count;
162         unsigned int leaf_node_size;
163         unsigned int leaf_block;
164         __u64 leaf_off;
165         unsigned int alloc_block_size;
166         unsigned int alloc_first_block;
167         unsigned int embed_first_block;
168         unsigned int record_count;
169         struct hfsplus_bnode_descriptor *descr;
170         struct hfsplus_bheader_record *bnode;
171         struct hfsplus_catalog_key *key;
172         unsigned int    label_len;
173         struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
174         const __u8 *buf;
175
176         dbg("probing at offset %llu", off);
177
178         buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
179         if (buf == NULL)
180                 return -1;
181
182         hfs = (struct hfs_mdb *) buf;
183         if (memcmp(hfs->signature, "BD", 2) != 0)
184                 goto checkplus;
185
186         /* it may be just a hfs wrapper for hfs+ */
187         if (memcmp(hfs->embed_sig, "H+", 2) == 0) {
188                 alloc_block_size = be32_to_cpu(hfs->al_blk_size);
189                 dbg("alloc_block_size 0x%x", alloc_block_size);
190
191                 alloc_first_block = be16_to_cpu(hfs->al_bl_st);
192                 dbg("alloc_first_block 0x%x", alloc_first_block);
193
194                 embed_first_block = be16_to_cpu(hfs->embed_startblock);
195                 dbg("embed_first_block 0x%x", embed_first_block);
196
197                 off += (alloc_first_block * 512) +
198                        (embed_first_block * alloc_block_size);
199                 dbg("hfs wrapped hfs+ found at offset 0x%llx", off);
200
201                 buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
202                 if (buf == NULL)
203                         return -1;
204                 goto checkplus;
205         }
206
207         if (hfs->label_len > 0 && hfs->label_len < 28) {
208                 volume_id_set_label_raw(id, hfs->label, hfs->label_len);
209                 volume_id_set_label_string(id, hfs->label, hfs->label_len) ;
210         }
211
212         volume_id_set_uuid(id, hfs->finder_info.id, UUID_HFS);
213
214         volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
215         id->type = "hfs";
216
217         return 0;
218
219 checkplus:
220         hfsplus = (struct hfsplus_vol_header *) buf;
221         if (memcmp(hfsplus->signature, "H+", 2) == 0)
222                 goto hfsplus;
223         if (memcmp(hfsplus->signature, "HX", 2) == 0)
224                 goto hfsplus;
225         return -1;
226
227 hfsplus:
228         volume_id_set_uuid(id, hfsplus->finder_info.id, UUID_HFS);
229
230         blocksize = be32_to_cpu(hfsplus->blocksize);
231         dbg("blocksize %u", blocksize);
232
233         memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
234         cat_block = be32_to_cpu(extents[0].start_block);
235         dbg("catalog start block 0x%x", cat_block);
236
237         buf = volume_id_get_buffer(id, off + (cat_block * blocksize), 0x2000);
238         if (buf == NULL)
239                 goto found;
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         dbg("catalog leaf node 0x%x", leaf_node_head);
246
247         leaf_node_size = be16_to_cpu(bnode->node_size);
248         dbg("leaf node size 0x%x", leaf_node_size);
249
250         leaf_node_count = be32_to_cpu(bnode->leaf_count);
251         dbg("leaf node count 0x%x", leaf_node_count);
252         if (leaf_node_count == 0)
253                 goto found;
254
255         leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
256
257         /* get physical location */
258         for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
259                 ext_block_start = be32_to_cpu(extents[ext].start_block);
260                 ext_block_count = be32_to_cpu(extents[ext].block_count);
261                 dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count);
262
263                 if (ext_block_count == 0)
264                         goto found;
265
266                 /* this is our extent */
267                 if (leaf_block < ext_block_count)
268                         break;
269
270                 leaf_block -= ext_block_count;
271         }
272         if (ext == HFSPLUS_EXTENT_COUNT)
273                 goto found;
274         dbg("found block in extent %i", ext);
275
276         leaf_off = (ext_block_start + leaf_block) * blocksize;
277
278         buf = volume_id_get_buffer(id, off + leaf_off, leaf_node_size);
279         if (buf == NULL)
280                 goto found;
281
282         descr = (struct hfsplus_bnode_descriptor *) buf;
283         dbg("descriptor type 0x%x", descr->type);
284
285         record_count = be16_to_cpu(descr->num_recs);
286         dbg("number of records %u", record_count);
287         if (record_count == 0)
288                 goto found;
289
290         if (descr->type != HFS_NODE_LEAF)
291                 goto found;
292
293         key = (struct hfsplus_catalog_key *)
294                 &buf[sizeof(struct hfsplus_bnode_descriptor)];
295
296         dbg("parent id 0x%x", be32_to_cpu(key->parent_id));
297         if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
298                 goto found;
299
300         label_len = be16_to_cpu(key->unicode_len) * 2;
301         dbg("label unicode16 len %i", label_len);
302         volume_id_set_label_raw(id, key->unicode, label_len);
303         volume_id_set_label_unicode16(id, key->unicode, BE, label_len);
304
305 found:
306         volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
307         id->type = "hfsplus";
308
309         return 0;
310 }