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