123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2012 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- /* HFS+ is documented at http://developer.apple.com/technotes/tn/tn1150.html */
- #include <grub/hfsplus.h>
- #include <grub/dl.h>
- #include <grub/misc.h>
- #include <grub/mm.h>
- #include <grub/deflate.h>
- #include <grub/file.h>
- GRUB_MOD_LICENSE ("GPLv3+");
- /* big-endian. */
- struct grub_hfsplus_compress_header1
- {
- grub_uint32_t header_size;
- grub_uint32_t end_descriptor_offset;
- grub_uint32_t total_compressed_size_including_seek_blocks_and_header2;
- grub_uint32_t value_0x32;
- grub_uint8_t unused[0xf0];
- } GRUB_PACKED;
- /* big-endian. */
- struct grub_hfsplus_compress_header2
- {
- grub_uint32_t total_compressed_size_including_seek_blocks;
- } GRUB_PACKED;
- /* little-endian. */
- struct grub_hfsplus_compress_header3
- {
- grub_uint32_t num_chunks;
- } GRUB_PACKED;
- /* little-endian. */
- struct grub_hfsplus_compress_block_descriptor
- {
- grub_uint32_t offset;
- grub_uint32_t size;
- };
- struct grub_hfsplus_compress_end_descriptor
- {
- grub_uint8_t always_the_same[50];
- } GRUB_PACKED;
- struct grub_hfsplus_attr_header
- {
- grub_uint8_t unused[3];
- grub_uint8_t type;
- grub_uint32_t unknown[1];
- grub_uint64_t size;
- } GRUB_PACKED;
- struct grub_hfsplus_compress_attr
- {
- grub_uint32_t magic;
- grub_uint32_t type;
- grub_uint32_t uncompressed_inline_size;
- grub_uint32_t always_0;
- } GRUB_PACKED;
- enum
- {
- HFSPLUS_COMPRESSION_INLINE = 3,
- HFSPLUS_COMPRESSION_RESOURCE = 4
- };
- static int
- grub_hfsplus_cmp_attrkey (struct grub_hfsplus_key *keya,
- struct grub_hfsplus_key_internal *keyb)
- {
- struct grub_hfsplus_attrkey *attrkey_a = &keya->attrkey;
- struct grub_hfsplus_attrkey_internal *attrkey_b = &keyb->attrkey;
- grub_uint32_t aparent = grub_be_to_cpu32 (attrkey_a->cnid);
- grub_size_t len;
- int diff;
- if (aparent > attrkey_b->cnid)
- return 1;
- if (aparent < attrkey_b->cnid)
- return -1;
- len = grub_be_to_cpu16 (attrkey_a->namelen);
- if (len > attrkey_b->namelen)
- len = attrkey_b->namelen;
- /* Since it's big-endian memcmp gives the same result as manually comparing
- uint16_t but may be faster. */
- diff = grub_memcmp (attrkey_a->name, attrkey_b->name,
- len * sizeof (attrkey_a->name[0]));
- if (diff == 0)
- diff = grub_be_to_cpu16 (attrkey_a->namelen) - attrkey_b->namelen;
- return diff;
- }
- #define HFSPLUS_COMPRESS_BLOCK_SIZE 65536
- static grub_ssize_t
- hfsplus_read_compressed_real (struct grub_hfsplus_file *node,
- grub_off_t pos, grub_size_t len, char *buf)
- {
- char *tmp_buf = 0;
- grub_size_t len0 = len;
- if (node->compressed == 1)
- {
- grub_memcpy (buf, node->cbuf + pos, len);
- if (grub_file_progress_hook && node->file)
- grub_file_progress_hook (0, 0, len, NULL, node->file);
- return len;
- }
- while (len)
- {
- grub_uint32_t block = pos / HFSPLUS_COMPRESS_BLOCK_SIZE;
- grub_size_t curlen = HFSPLUS_COMPRESS_BLOCK_SIZE
- - (pos % HFSPLUS_COMPRESS_BLOCK_SIZE);
- if (curlen > len)
- curlen = len;
- if (node->cbuf_block != block)
- {
- grub_uint32_t sz = grub_le_to_cpu32 (node->compress_index[block].size);
- grub_size_t ts;
- if (!tmp_buf)
- tmp_buf = grub_malloc (HFSPLUS_COMPRESS_BLOCK_SIZE);
- if (!tmp_buf)
- return -1;
- if (grub_hfsplus_read_file (node, 0, 0,
- grub_le_to_cpu32 (node->compress_index[block].start) + 0x104,
- sz, tmp_buf)
- != (grub_ssize_t) sz)
- {
- grub_free (tmp_buf);
- return -1;
- }
- ts = HFSPLUS_COMPRESS_BLOCK_SIZE;
- if (ts > node->size - (pos & ~(HFSPLUS_COMPRESS_BLOCK_SIZE)))
- ts = node->size - (pos & ~(HFSPLUS_COMPRESS_BLOCK_SIZE));
- if (grub_zlib_decompress (tmp_buf, sz, 0,
- node->cbuf, ts) != (grub_ssize_t) ts)
- {
- if (!grub_errno)
- grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
- "premature end of compressed");
- grub_free (tmp_buf);
- return -1;
- }
- node->cbuf_block = block;
- }
- grub_memcpy (buf, node->cbuf + (pos % HFSPLUS_COMPRESS_BLOCK_SIZE),
- curlen);
- if (grub_file_progress_hook && node->file)
- grub_file_progress_hook (0, 0, curlen, NULL, node->file);
- buf += curlen;
- pos += curlen;
- len -= curlen;
- }
- grub_free (tmp_buf);
- return len0;
- }
- static grub_err_t
- hfsplus_open_compressed_real (struct grub_hfsplus_file *node)
- {
- grub_err_t err;
- struct grub_hfsplus_btnode *attr_node;
- grub_off_t attr_off;
- struct grub_hfsplus_key_internal key;
- struct grub_hfsplus_attr_header *attr_head;
- struct grub_hfsplus_compress_attr *cmp_head;
- #define c grub_cpu_to_be16_compile_time
- const grub_uint16_t compress_attr_name[] =
- {
- c('c'), c('o'), c('m'), c('.'), c('a'), c('p'), c('p'), c('l'), c('e'),
- c('.'), c('d'), c('e'), c('c'), c('m'), c('p'), c('f'), c('s') };
- #undef c
- if (node->size)
- return 0;
- key.attrkey.cnid = node->fileid;
- key.attrkey.namelen = sizeof (compress_attr_name) / sizeof (compress_attr_name[0]);
- key.attrkey.name = compress_attr_name;
- err = grub_hfsplus_btree_search (&node->data->attr_tree, &key,
- grub_hfsplus_cmp_attrkey,
- &attr_node, &attr_off);
- if (err || !attr_node)
- {
- grub_errno = 0;
- return 0;
- }
- attr_head = (struct grub_hfsplus_attr_header *)
- ((char *) grub_hfsplus_btree_recptr (&node->data->attr_tree,
- attr_node, attr_off)
- + sizeof (struct grub_hfsplus_attrkey) + sizeof (compress_attr_name));
- if (attr_head->type != 0x10
- || !(attr_head->size & grub_cpu_to_be64_compile_time(~0xfULL)))
- {
- grub_free (attr_node);
- return 0;
- }
- cmp_head = (struct grub_hfsplus_compress_attr *) (attr_head + 1);
- if (cmp_head->magic != grub_cpu_to_be32_compile_time (0x66706d63))
- {
- grub_free (attr_node);
- return 0;
- }
- node->size = grub_le_to_cpu32 (cmp_head->uncompressed_inline_size);
- if (cmp_head->type == grub_cpu_to_le32_compile_time (HFSPLUS_COMPRESSION_RESOURCE))
- {
- grub_uint32_t index_size;
- node->compressed = 2;
- if (grub_hfsplus_read_file (node, 0, 0,
- 0x104, sizeof (index_size),
- (char *) &index_size)
- != 4)
- {
- node->compressed = 0;
- grub_free (attr_node);
- grub_errno = 0;
- return 0;
- }
- node->compress_index_size = grub_le_to_cpu32 (index_size);
- node->compress_index = grub_malloc (node->compress_index_size
- * sizeof (node->compress_index[0]));
- if (!node->compress_index)
- {
- node->compressed = 0;
- grub_free (attr_node);
- return grub_errno;
- }
- if (grub_hfsplus_read_file (node, 0, 0,
- 0x104 + sizeof (index_size),
- node->compress_index_size
- * sizeof (node->compress_index[0]),
- (char *) node->compress_index)
- != (grub_ssize_t) (node->compress_index_size
- * sizeof (node->compress_index[0])))
- {
- node->compressed = 0;
- grub_free (attr_node);
- grub_free (node->compress_index);
- grub_errno = 0;
- return 0;
- }
- node->cbuf_block = -1;
- node->cbuf = grub_malloc (HFSPLUS_COMPRESS_BLOCK_SIZE);
- grub_free (attr_node);
- if (!node->cbuf)
- {
- node->compressed = 0;
- grub_free (node->compress_index);
- return grub_errno;
- }
- return 0;
- }
- if (cmp_head->type != HFSPLUS_COMPRESSION_INLINE)
- {
- grub_free (attr_node);
- return 0;
- }
- node->cbuf = grub_malloc (node->size);
- if (!node->cbuf)
- return grub_errno;
- if (grub_zlib_decompress ((char *) (cmp_head + 1),
- grub_cpu_to_be64 (attr_head->size)
- - sizeof (*cmp_head), 0,
- node->cbuf, node->size)
- != (grub_ssize_t) node->size)
- {
- if (!grub_errno)
- grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
- "premature end of compressed");
- return grub_errno;
- }
- node->compressed = 1;
- return 0;
- }
- GRUB_MOD_INIT(hfspluscomp)
- {
- grub_hfsplus_open_compressed = hfsplus_open_compressed_real;
- grub_hfsplus_read_compressed = hfsplus_read_compressed_real;
- }
- GRUB_MOD_FINI(hfspluscomp)
- {
- grub_hfsplus_open_compressed = 0;
- grub_hfsplus_read_compressed = 0;
- }
|