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