2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2012 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 /* HFS+ is documented at http://developer.apple.com/technotes/tn/tn1150.html */
21 #include <grub/hfsplus.h>
23 #include <grub/misc.h>
25 #include <grub/deflate.h>
26 #include <grub/file.h>
28 GRUB_MOD_LICENSE ("GPLv3+");
31 struct grub_hfsplus_compress_header1
33 grub_uint32_t header_size
;
34 grub_uint32_t end_descriptor_offset
;
35 grub_uint32_t total_compressed_size_including_seek_blocks_and_header2
;
36 grub_uint32_t value_0x32
;
37 grub_uint8_t unused
[0xf0];
41 struct grub_hfsplus_compress_header2
43 grub_uint32_t total_compressed_size_including_seek_blocks
;
47 struct grub_hfsplus_compress_header3
49 grub_uint32_t num_chunks
;
53 struct grub_hfsplus_compress_block_descriptor
59 struct grub_hfsplus_compress_end_descriptor
61 grub_uint8_t always_the_same
[50];
64 struct grub_hfsplus_attr_header
66 grub_uint8_t unused
[3];
68 grub_uint32_t unknown
[1];
72 struct grub_hfsplus_compress_attr
76 grub_uint32_t uncompressed_inline_size
;
77 grub_uint32_t always_0
;
82 HFSPLUS_COMPRESSION_INLINE
= 3,
83 HFSPLUS_COMPRESSION_RESOURCE
= 4
87 grub_hfsplus_cmp_attrkey (struct grub_hfsplus_key
*keya
,
88 struct grub_hfsplus_key_internal
*keyb
)
90 struct grub_hfsplus_attrkey
*attrkey_a
= &keya
->attrkey
;
91 struct grub_hfsplus_attrkey_internal
*attrkey_b
= &keyb
->attrkey
;
92 grub_uint32_t aparent
= grub_be_to_cpu32 (attrkey_a
->cnid
);
96 if (aparent
> attrkey_b
->cnid
)
98 if (aparent
< attrkey_b
->cnid
)
101 len
= grub_be_to_cpu16 (attrkey_a
->namelen
);
102 if (len
> attrkey_b
->namelen
)
103 len
= attrkey_b
->namelen
;
104 /* Since it's big-endian memcmp gives the same result as manually comparing
105 uint16_t but may be faster. */
106 diff
= grub_memcmp (attrkey_a
->name
, attrkey_b
->name
,
107 len
* sizeof (attrkey_a
->name
[0]));
109 diff
= grub_be_to_cpu16 (attrkey_a
->namelen
) - attrkey_b
->namelen
;
113 #define HFSPLUS_COMPRESS_BLOCK_SIZE 65536
116 hfsplus_read_compressed_real (struct grub_hfsplus_file
*node
,
117 grub_off_t pos
, grub_size_t len
, char *buf
)
120 grub_size_t len0
= len
;
122 if (node
->compressed
== 1)
124 grub_memcpy (buf
, node
->cbuf
+ pos
, len
);
125 if (grub_file_progress_hook
&& node
->file
)
126 grub_file_progress_hook (0, 0, len
, node
->file
);
132 grub_uint32_t block
= pos
/ HFSPLUS_COMPRESS_BLOCK_SIZE
;
133 grub_size_t curlen
= HFSPLUS_COMPRESS_BLOCK_SIZE
134 - (pos
% HFSPLUS_COMPRESS_BLOCK_SIZE
);
139 if (node
->cbuf_block
!= block
)
141 grub_uint32_t sz
= grub_le_to_cpu32 (node
->compress_index
[block
].size
);
144 tmp_buf
= grub_malloc (HFSPLUS_COMPRESS_BLOCK_SIZE
);
147 if (grub_hfsplus_read_file (node
, 0, 0,
148 grub_le_to_cpu32 (node
->compress_index
[block
].start
) + 0x104,
150 != (grub_ssize_t
) sz
)
155 ts
= HFSPLUS_COMPRESS_BLOCK_SIZE
;
156 if (ts
> node
->size
- (pos
& ~(HFSPLUS_COMPRESS_BLOCK_SIZE
)))
157 ts
= node
->size
- (pos
& ~(HFSPLUS_COMPRESS_BLOCK_SIZE
));
158 if (grub_zlib_decompress (tmp_buf
, sz
, 0,
159 node
->cbuf
, ts
) != (grub_ssize_t
) ts
)
162 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA
,
163 "premature end of compressed");
168 node
->cbuf_block
= block
;
170 grub_memcpy (buf
, node
->cbuf
+ (pos
% HFSPLUS_COMPRESS_BLOCK_SIZE
),
172 if (grub_file_progress_hook
&& node
->file
)
173 grub_file_progress_hook (0, 0, curlen
, node
->file
);
183 hfsplus_open_compressed_real (struct grub_hfsplus_file
*node
)
186 struct grub_hfsplus_btnode
*attr_node
;
188 struct grub_hfsplus_key_internal key
;
189 struct grub_hfsplus_attr_header
*attr_head
;
190 struct grub_hfsplus_compress_attr
*cmp_head
;
191 #define c grub_cpu_to_be16_compile_time
192 const grub_uint16_t compress_attr_name
[] =
194 c('c'), c('o'), c('m'), c('.'), c('a'), c('p'), c('p'), c('l'), c('e'),
195 c('.'), c('d'), c('e'), c('c'), c('m'), c('p'), c('f'), c('s') };
200 key
.attrkey
.cnid
= node
->fileid
;
201 key
.attrkey
.namelen
= sizeof (compress_attr_name
) / sizeof (compress_attr_name
[0]);
202 key
.attrkey
.name
= compress_attr_name
;
204 err
= grub_hfsplus_btree_search (&node
->data
->attr_tree
, &key
,
205 grub_hfsplus_cmp_attrkey
,
206 &attr_node
, &attr_off
);
207 if (err
|| !attr_node
)
213 attr_head
= (struct grub_hfsplus_attr_header
*)
214 ((char *) grub_hfsplus_btree_recptr (&node
->data
->attr_tree
,
216 + sizeof (struct grub_hfsplus_attrkey
) + sizeof (compress_attr_name
));
217 if (attr_head
->type
!= 0x10
218 || !(attr_head
->size
& grub_cpu_to_be64_compile_time(~0xfULL
)))
220 grub_free (attr_node
);
223 cmp_head
= (struct grub_hfsplus_compress_attr
*) (attr_head
+ 1);
224 if (cmp_head
->magic
!= grub_cpu_to_be32_compile_time (0x66706d63))
226 grub_free (attr_node
);
229 node
->size
= grub_le_to_cpu32 (cmp_head
->uncompressed_inline_size
);
231 if (cmp_head
->type
== grub_cpu_to_le32_compile_time (HFSPLUS_COMPRESSION_RESOURCE
))
233 grub_uint32_t index_size
;
234 node
->compressed
= 2;
236 if (grub_hfsplus_read_file (node
, 0, 0,
237 0x104, sizeof (index_size
),
238 (char *) &index_size
)
241 node
->compressed
= 0;
242 grub_free (attr_node
);
246 node
->compress_index_size
= grub_le_to_cpu32 (index_size
);
247 node
->compress_index
= grub_malloc (node
->compress_index_size
248 * sizeof (node
->compress_index
[0]));
249 if (!node
->compress_index
)
251 node
->compressed
= 0;
252 grub_free (attr_node
);
255 if (grub_hfsplus_read_file (node
, 0, 0,
256 0x104 + sizeof (index_size
),
257 node
->compress_index_size
258 * sizeof (node
->compress_index
[0]),
259 (char *) node
->compress_index
)
260 != (grub_ssize_t
) (node
->compress_index_size
261 * sizeof (node
->compress_index
[0])))
263 node
->compressed
= 0;
264 grub_free (attr_node
);
265 grub_free (node
->compress_index
);
270 node
->cbuf_block
= -1;
272 node
->cbuf
= grub_malloc (HFSPLUS_COMPRESS_BLOCK_SIZE
);
273 grub_free (attr_node
);
276 node
->compressed
= 0;
277 grub_free (node
->compress_index
);
282 if (cmp_head
->type
!= HFSPLUS_COMPRESSION_INLINE
)
284 grub_free (attr_node
);
288 node
->cbuf
= grub_malloc (node
->size
);
292 if (grub_zlib_decompress ((char *) (cmp_head
+ 1),
293 grub_cpu_to_be64 (attr_head
->size
)
294 - sizeof (*cmp_head
), 0,
295 node
->cbuf
, node
->size
)
296 != (grub_ssize_t
) node
->size
)
299 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA
,
300 "premature end of compressed");
303 node
->compressed
= 1;
307 GRUB_MOD_INIT(hfspluscomp
)
309 grub_hfsplus_open_compressed
= hfsplus_open_compressed_real
;
310 grub_hfsplus_read_compressed
= hfsplus_read_compressed_real
;
313 GRUB_MOD_FINI(hfspluscomp
)
315 grub_hfsplus_open_compressed
= 0;
316 grub_hfsplus_read_compressed
= 0;