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