--- /dev/null
-};
+ /*
+ * 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>
+
+ 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];
+ } __attribute__ ((packed));
+
+ /* big-endian. */
+ struct grub_hfsplus_compress_header2
+ {
+ grub_uint32_t total_compressed_size_including_seek_blocks;
+ } __attribute__ ((packed));
+
+ /* little-endian. */
+ struct grub_hfsplus_compress_header3
+ {
+ grub_uint32_t num_chunks;
+ } __attribute__ ((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];
+ } __attribute__ ((packed));
+
+ struct grub_hfsplus_attr_header
+ {
+ grub_uint8_t unused[3];
+ grub_uint8_t type;
+ grub_uint32_t unknown[1];
+ grub_uint64_t size;
+ } __attribute__ ((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;
++} __attribute__ ((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);
+ 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) < 0)
+ {
+ grub_free (tmp_buf);
+ return -1;
+ }
+ node->cbuf_block = block;
+ }
+ grub_memcpy (buf, node->cbuf + (pos % HFSPLUS_COMPRESS_BLOCK_SIZE),
+ curlen);
+ 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) < 0)
+ 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;
+ }
--- /dev/null
+ /*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/>.
+ */
+
+ #include <grub/test.h>
+ #include <grub/dl.h>
+ #include <grub/setjmp.h>
+
+ GRUB_MOD_LICENSE ("GPLv3+");
+
+ static grub_jmp_buf jmp_point;
+ static int expected, ctr;
+
++#pragma GCC diagnostic ignored "-Wmissing-noreturn"
++
+ static void
+ jmp0 (void)
+ {
+ grub_longjmp (jmp_point, 0);
+ }
+
+ static void
+ jmp1 (void)
+ {
+ grub_longjmp (jmp_point, 1);
+ }
+
+ static void
+ jmp2 (void)
+ {
+ grub_longjmp (jmp_point, 2);
+ }
+
+ static void
+ setjmp_test (void)
+ {
+ int val;
+
+ expected = 0;
+ val = grub_setjmp (jmp_point);
+
+ grub_test_assert (val == expected, "setjmp returned %d instead of %d",
+ val, expected);
+ switch (ctr++)
+ {
+ case 0:
+ expected = 1;
+ jmp0 ();
+ case 1:
+ expected = 1;
+ jmp1 ();
+ case 2:
+ expected = 2;
+ jmp2 ();
+ case 3:
+ return;
+ }
+ grub_test_assert (0, "setjmp didn't return enough times");
+ }
+
+ /* Register example_test method as a functional test. */
+ GRUB_FUNCTIONAL_TEST (setjmp_test, setjmp_test);