/* Search for the block FILEBLOCK inside the file NODE. Return the blocknumber of this block on disk. */ static grub_disk_addr_t grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_hfsplus_btnode *nnode = 0; grub_disk_addr_t blksleft = fileblock; struct grub_hfsplus_extent *extents = &node->extents[0]; while (1) { struct grub_hfsplus_extkey *key; struct grub_hfsplus_key_internal extoverflow; grub_disk_addr_t blk; grub_off_t ptr; /* Try to find this block in the current set of extents. */ blk = grub_hfsplus_find_block (extents, &blksleft); /* The previous iteration of this loop allocated memory. The code above used this memory, it can be freed now. */ grub_free (nnode); nnode = 0; if (blk != 0xffffffffffffffffULL) return blk; /* For the extent overflow file, extra extents can't be found in the extent overflow file. If this happens, you found a bug... */ if (node->fileid == GRUB_HFSPLUS_FILEID_OVERFLOW) { grub_error (GRUB_ERR_READ_ERROR, "extra extents found in an extend overflow file"); break; } /* Set up the key to look for in the extent overflow file. */ extoverflow.extkey.fileid = node->fileid; extoverflow.extkey.type = 0; extoverflow.extkey.start = fileblock - blksleft; if (grub_hfsplus_btree_search (&node->data->extoverflow_tree, &extoverflow, grub_hfsplus_cmp_extkey, &nnode, &ptr)) { grub_error (GRUB_ERR_READ_ERROR, "no block found for the file id 0x%x and the block offset 0x%x", node->fileid, fileblock); break; } /* The extent overflow file has 8 extents right after the key. */ key = (struct grub_hfsplus_extkey *) grub_hfsplus_btree_recptr (&node->data->extoverflow_tree, nnode, ptr); extents = (struct grub_hfsplus_extent *) (key + 1); /* The block wasn't found. Perhaps the next iteration will find it. The last block we found is stored in BLKSLEFT now. */ } grub_free (nnode); /* Too bad, you lose. */ return -1; }
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; }