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