Big cleanup in config help and description
[oweals/busybox.git] / util-linux / volume_id / 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 //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_HFS) += hfs.o
22
23 //config:config FEATURE_VOLUMEID_HFS
24 //config:       bool "hfs filesystem"
25 //config:       default y
26 //config:       depends on VOLUMEID
27
28 #include "volume_id_internal.h"
29
30 struct hfs_finder_info{
31         uint32_t        boot_folder;
32         uint32_t        start_app;
33         uint32_t        open_folder;
34         uint32_t        os9_folder;
35         uint32_t        reserved;
36         uint32_t        osx_folder;
37         uint8_t         id[8];
38 } PACKED;
39
40 struct hfs_mdb {
41         uint8_t         signature[2];
42         uint32_t        cr_date;
43         uint32_t        ls_Mod;
44         uint16_t        atrb;
45         uint16_t        nm_fls;
46         uint16_t        vbm_st;
47         uint16_t        alloc_ptr;
48         uint16_t        nm_al_blks;
49         uint32_t        al_blk_size;
50         uint32_t        clp_size;
51         uint16_t        al_bl_st;
52         uint32_t        nxt_cnid;
53         uint16_t        free_bks;
54         uint8_t         label_len;
55         uint8_t         label[27];
56         uint32_t        vol_bkup;
57         uint16_t        vol_seq_num;
58         uint32_t        wr_cnt;
59         uint32_t        xt_clump_size;
60         uint32_t        ct_clump_size;
61         uint16_t        num_root_dirs;
62         uint32_t        file_count;
63         uint32_t        dir_count;
64         struct hfs_finder_info finder_info;
65         uint8_t         embed_sig[2];
66         uint16_t        embed_startblock;
67         uint16_t        embed_blockcount;
68 } PACKED;
69
70 struct hfsplus_bnode_descriptor {
71         uint32_t        next;
72         uint32_t        prev;
73         uint8_t         type;
74         uint8_t         height;
75         uint16_t        num_recs;
76         uint16_t        reserved;
77 } PACKED;
78
79 struct hfsplus_bheader_record {
80         uint16_t        depth;
81         uint32_t        root;
82         uint32_t        leaf_count;
83         uint32_t        leaf_head;
84         uint32_t        leaf_tail;
85         uint16_t        node_size;
86 } PACKED;
87
88 struct hfsplus_catalog_key {
89         uint16_t        key_len;
90         uint32_t        parent_id;
91         uint16_t        unicode_len;
92         uint8_t         unicode[255 * 2];
93 } PACKED;
94
95 struct hfsplus_extent {
96         uint32_t        start_block;
97         uint32_t        block_count;
98 } PACKED;
99
100 #define HFSPLUS_EXTENT_COUNT            8
101 struct hfsplus_fork {
102         uint64_t        total_size;
103         uint32_t        clump_size;
104         uint32_t        total_blocks;
105         struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
106 } PACKED;
107
108 struct hfsplus_vol_header {
109         uint8_t         signature[2];
110         uint16_t        version;
111         uint32_t        attributes;
112         uint32_t        last_mount_vers;
113         uint32_t        reserved;
114         uint32_t        create_date;
115         uint32_t        modify_date;
116         uint32_t        backup_date;
117         uint32_t        checked_date;
118         uint32_t        file_count;
119         uint32_t        folder_count;
120         uint32_t        blocksize;
121         uint32_t        total_blocks;
122         uint32_t        free_blocks;
123         uint32_t        next_alloc;
124         uint32_t        rsrc_clump_sz;
125         uint32_t        data_clump_sz;
126         uint32_t        next_cnid;
127         uint32_t        write_count;
128         uint64_t        encodings_bmp;
129         struct hfs_finder_info finder_info;
130         struct hfsplus_fork alloc_file;
131         struct hfsplus_fork ext_file;
132         struct hfsplus_fork cat_file;
133         struct hfsplus_fork attr_file;
134         struct hfsplus_fork start_file;
135 } PACKED;
136
137 #define HFS_SUPERBLOCK_OFFSET           0x400
138 #define HFS_NODE_LEAF                   0xff
139 #define HFSPLUS_POR_CNID                1
140
141 static void FAST_FUNC hfs_set_uuid(struct volume_id *id, const uint8_t *hfs_id)
142 {
143 #define hfs_id_len 8
144         md5_ctx_t md5c;
145         uint8_t uuid[16];
146         unsigned i;
147
148         for (i = 0; i < hfs_id_len; i++)
149                 if (hfs_id[i] != 0)
150                         goto do_md5;
151         return;
152  do_md5:
153         md5_begin(&md5c);
154         md5_hash(&md5c, "\263\342\17\71\362\222\21\326\227\244\0\60\145\103\354\254", 16);
155         md5_hash(&md5c, hfs_id, hfs_id_len);
156         md5_end(&md5c, uuid);
157         uuid[6] = 0x30 | (uuid[6] & 0x0f);
158         uuid[8] = 0x80 | (uuid[8] & 0x3f);
159         volume_id_set_uuid(id, uuid, UUID_DCE);
160 }
161
162 int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/)
163 {
164         uint64_t off = 0;
165         unsigned blocksize;
166         unsigned cat_block;
167         unsigned ext_block_start;
168         unsigned ext_block_count;
169         int ext;
170         unsigned leaf_node_head;
171         unsigned leaf_node_count;
172         unsigned leaf_node_size;
173         unsigned leaf_block;
174         uint64_t leaf_off;
175         unsigned alloc_block_size;
176         unsigned alloc_first_block;
177         unsigned embed_first_block;
178         unsigned record_count;
179         struct hfsplus_vol_header *hfsplus;
180         struct hfsplus_bnode_descriptor *descr;
181         struct hfsplus_bheader_record *bnode;
182         struct hfsplus_catalog_key *key;
183         unsigned label_len;
184         struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
185         struct hfs_mdb *hfs;
186         const uint8_t *buf;
187
188         dbg("probing at offset 0x%llx", (unsigned long long) off);
189
190         buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
191         if (buf == NULL)
192                 return -1;
193
194         hfs = (struct hfs_mdb *) buf;
195         if (hfs->signature[0] != 'B' || hfs->signature[1] != 'D')
196                 goto checkplus;
197
198         /* it may be just a hfs wrapper for hfs+ */
199         if (hfs->embed_sig[0] == 'H' && hfs->embed_sig[1] == '+') {
200                 alloc_block_size = be32_to_cpu(hfs->al_blk_size);
201                 dbg("alloc_block_size 0x%x", alloc_block_size);
202
203                 alloc_first_block = be16_to_cpu(hfs->al_bl_st);
204                 dbg("alloc_first_block 0x%x", alloc_first_block);
205
206                 embed_first_block = be16_to_cpu(hfs->embed_startblock);
207                 dbg("embed_first_block 0x%x", embed_first_block);
208
209                 off += (alloc_first_block * 512) +
210                        (embed_first_block * alloc_block_size);
211                 dbg("hfs wrapped hfs+ found at offset 0x%llx", (unsigned long long) off);
212
213                 buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
214                 if (buf == NULL)
215                         return -1;
216                 goto checkplus;
217         }
218
219         if (hfs->label_len > 0 && hfs->label_len < 28) {
220 //              volume_id_set_label_raw(id, hfs->label, hfs->label_len);
221                 volume_id_set_label_string(id, hfs->label, hfs->label_len) ;
222         }
223
224         hfs_set_uuid(id, hfs->finder_info.id);
225 //      volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
226         IF_FEATURE_BLKID_TYPE(id->type = "hfs";)
227
228         return 0;
229
230  checkplus:
231         hfsplus = (struct hfsplus_vol_header *) buf;
232         if (hfs->signature[0] == 'H')
233                 if (hfs->signature[1] == '+' || hfs->signature[1] == 'X')
234                         goto hfsplus;
235         return -1;
236
237  hfsplus:
238         hfs_set_uuid(id, hfsplus->finder_info.id);
239
240         blocksize = be32_to_cpu(hfsplus->blocksize);
241         dbg("blocksize %u", blocksize);
242
243         memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
244         cat_block = be32_to_cpu(extents[0].start_block);
245         dbg("catalog start block 0x%x", cat_block);
246
247         buf = volume_id_get_buffer(id, off + (cat_block * blocksize), 0x2000);
248         if (buf == NULL)
249                 goto found;
250
251         bnode = (struct hfsplus_bheader_record *)
252                 &buf[sizeof(struct hfsplus_bnode_descriptor)];
253
254         leaf_node_head = be32_to_cpu(bnode->leaf_head);
255         dbg("catalog leaf node 0x%x", leaf_node_head);
256
257         leaf_node_size = be16_to_cpu(bnode->node_size);
258         dbg("leaf node size 0x%x", leaf_node_size);
259
260         leaf_node_count = be32_to_cpu(bnode->leaf_count);
261         dbg("leaf node count 0x%x", leaf_node_count);
262         if (leaf_node_count == 0)
263                 goto found;
264
265         leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
266
267         /* get physical location */
268         for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
269                 ext_block_start = be32_to_cpu(extents[ext].start_block);
270                 ext_block_count = be32_to_cpu(extents[ext].block_count);
271                 dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count);
272
273                 if (ext_block_count == 0)
274                         goto found;
275
276                 /* this is our extent */
277                 if (leaf_block < ext_block_count)
278                         break;
279
280                 leaf_block -= ext_block_count;
281         }
282         if (ext == HFSPLUS_EXTENT_COUNT)
283                 goto found;
284         dbg("found block in extent %i", ext);
285
286         leaf_off = (ext_block_start + leaf_block) * blocksize;
287
288         buf = volume_id_get_buffer(id, off + leaf_off, leaf_node_size);
289         if (buf == NULL)
290                 goto found;
291
292         descr = (struct hfsplus_bnode_descriptor *) buf;
293         dbg("descriptor type 0x%x", descr->type);
294
295         record_count = be16_to_cpu(descr->num_recs);
296         dbg("number of records %u", record_count);
297         if (record_count == 0)
298                 goto found;
299
300         if (descr->type != HFS_NODE_LEAF)
301                 goto found;
302
303         key = (struct hfsplus_catalog_key *)
304                 &buf[sizeof(struct hfsplus_bnode_descriptor)];
305
306         dbg("parent id 0x%x", be32_to_cpu(key->parent_id));
307         if (key->parent_id != cpu_to_be32(HFSPLUS_POR_CNID))
308                 goto found;
309
310         label_len = be16_to_cpu(key->unicode_len) * 2;
311         dbg("label unicode16 len %i", label_len);
312 //      volume_id_set_label_raw(id, key->unicode, label_len);
313         volume_id_set_label_unicode16(id, key->unicode, BE, label_len);
314
315  found:
316 //      volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
317         IF_FEATURE_BLKID_TYPE(id->type = "hfsplus";)
318
319         return 0;
320 }