diff options
Diffstat (limited to 'qemu/block/dmg.c')
-rw-r--r-- | qemu/block/dmg.c | 727 |
1 files changed, 0 insertions, 727 deletions
diff --git a/qemu/block/dmg.c b/qemu/block/dmg.c deleted file mode 100644 index a496eb7c9..000000000 --- a/qemu/block/dmg.c +++ /dev/null @@ -1,727 +0,0 @@ -/* - * QEMU Block driver for DMG images - * - * Copyright (c) 2004 Johannes E. Schindelin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "block/block_int.h" -#include "qemu/bswap.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include <zlib.h> -#ifdef CONFIG_BZIP2 -#include <bzlib.h> -#endif -#include <glib.h> - -enum { - /* Limit chunk sizes to prevent unreasonable amounts of memory being used - * or truncating when converting to 32-bit types - */ - DMG_LENGTHS_MAX = 64 * 1024 * 1024, /* 64 MB */ - DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, -}; - -typedef struct BDRVDMGState { - CoMutex lock; - /* each chunk contains a certain number of sectors, - * offsets[i] is the offset in the .dmg file, - * lengths[i] is the length of the compressed chunk, - * sectors[i] is the sector beginning at offsets[i], - * sectorcounts[i] is the number of sectors in that chunk, - * the sectors array is ordered - * 0<=i<n_chunks */ - - uint32_t n_chunks; - uint32_t* types; - uint64_t* offsets; - uint64_t* lengths; - uint64_t* sectors; - uint64_t* sectorcounts; - uint32_t current_chunk; - uint8_t *compressed_chunk; - uint8_t *uncompressed_chunk; - z_stream zstream; -#ifdef CONFIG_BZIP2 - bz_stream bzstream; -#endif -} BDRVDMGState; - -static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) -{ - int len; - - if (!filename) { - return 0; - } - - len = strlen(filename); - if (len > 4 && !strcmp(filename + len - 4, ".dmg")) { - return 2; - } - return 0; -} - -static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) -{ - uint64_t buffer; - int ret; - - ret = bdrv_pread(bs->file->bs, offset, &buffer, 8); - if (ret < 0) { - return ret; - } - - *result = be64_to_cpu(buffer); - return 0; -} - -static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) -{ - uint32_t buffer; - int ret; - - ret = bdrv_pread(bs->file->bs, offset, &buffer, 4); - if (ret < 0) { - return ret; - } - - *result = be32_to_cpu(buffer); - return 0; -} - -static inline uint64_t buff_read_uint64(const uint8_t *buffer, int64_t offset) -{ - return be64_to_cpu(*(uint64_t *)&buffer[offset]); -} - -static inline uint32_t buff_read_uint32(const uint8_t *buffer, int64_t offset) -{ - return be32_to_cpu(*(uint32_t *)&buffer[offset]); -} - -/* Increase max chunk sizes, if necessary. This function is used to calculate - * the buffer sizes needed for compressed/uncompressed chunk I/O. - */ -static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, - uint32_t *max_compressed_size, - uint32_t *max_sectors_per_chunk) -{ - uint32_t compressed_size = 0; - uint32_t uncompressed_sectors = 0; - - switch (s->types[chunk]) { - case 0x80000005: /* zlib compressed */ - case 0x80000006: /* bzip2 compressed */ - compressed_size = s->lengths[chunk]; - uncompressed_sectors = s->sectorcounts[chunk]; - break; - case 1: /* copy */ - uncompressed_sectors = (s->lengths[chunk] + 511) / 512; - break; - case 2: /* zero */ - /* as the all-zeroes block may be large, it is treated specially: the - * sector is not copied from a large buffer, a simple memset is used - * instead. Therefore uncompressed_sectors does not need to be set. */ - break; - } - - if (compressed_size > *max_compressed_size) { - *max_compressed_size = compressed_size; - } - if (uncompressed_sectors > *max_sectors_per_chunk) { - *max_sectors_per_chunk = uncompressed_sectors; - } -} - -static int64_t dmg_find_koly_offset(BlockDriverState *file_bs, Error **errp) -{ - int64_t length; - int64_t offset = 0; - uint8_t buffer[515]; - int i, ret; - - /* bdrv_getlength returns a multiple of block size (512), rounded up. Since - * dmg images can have odd sizes, try to look for the "koly" magic which - * marks the begin of the UDIF trailer (512 bytes). This magic can be found - * in the last 511 bytes of the second-last sector or the first 4 bytes of - * the last sector (search space: 515 bytes) */ - length = bdrv_getlength(file_bs); - if (length < 0) { - error_setg_errno(errp, -length, - "Failed to get file size while reading UDIF trailer"); - return length; - } else if (length < 512) { - error_setg(errp, "dmg file must be at least 512 bytes long"); - return -EINVAL; - } - if (length > 511 + 512) { - offset = length - 511 - 512; - } - length = length < 515 ? length : 515; - ret = bdrv_pread(file_bs, offset, buffer, length); - if (ret < 0) { - error_setg_errno(errp, -ret, "Failed while reading UDIF trailer"); - return ret; - } - for (i = 0; i < length - 3; i++) { - if (buffer[i] == 'k' && buffer[i+1] == 'o' && - buffer[i+2] == 'l' && buffer[i+3] == 'y') { - return offset + i; - } - } - error_setg(errp, "Could not locate UDIF trailer in dmg file"); - return -EINVAL; -} - -/* used when building the sector table */ -typedef struct DmgHeaderState { - /* used internally by dmg_read_mish_block to remember offsets of blocks - * across calls */ - uint64_t data_fork_offset; - /* exported for dmg_open */ - uint32_t max_compressed_size; - uint32_t max_sectors_per_chunk; -} DmgHeaderState; - -static bool dmg_is_known_block_type(uint32_t entry_type) -{ - switch (entry_type) { - case 0x00000001: /* uncompressed */ - case 0x00000002: /* zeroes */ - case 0x80000005: /* zlib */ -#ifdef CONFIG_BZIP2 - case 0x80000006: /* bzip2 */ -#endif - return true; - default: - return false; - } -} - -static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, - uint8_t *buffer, uint32_t count) -{ - uint32_t type, i; - int ret; - size_t new_size; - uint32_t chunk_count; - int64_t offset = 0; - uint64_t data_offset; - uint64_t in_offset = ds->data_fork_offset; - uint64_t out_offset; - - type = buff_read_uint32(buffer, offset); - /* skip data that is not a valid MISH block (invalid magic or too small) */ - if (type != 0x6d697368 || count < 244) { - /* assume success for now */ - return 0; - } - - /* chunk offsets are relative to this sector number */ - out_offset = buff_read_uint64(buffer, offset + 8); - - /* location in data fork for (compressed) blob (in bytes) */ - data_offset = buff_read_uint64(buffer, offset + 0x18); - in_offset += data_offset; - - /* move to begin of chunk entries */ - offset += 204; - - chunk_count = (count - 204) / 40; - new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); - s->types = g_realloc(s->types, new_size / 2); - s->offsets = g_realloc(s->offsets, new_size); - s->lengths = g_realloc(s->lengths, new_size); - s->sectors = g_realloc(s->sectors, new_size); - s->sectorcounts = g_realloc(s->sectorcounts, new_size); - - for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { - s->types[i] = buff_read_uint32(buffer, offset); - if (!dmg_is_known_block_type(s->types[i])) { - chunk_count--; - i--; - offset += 40; - continue; - } - - /* sector number */ - s->sectors[i] = buff_read_uint64(buffer, offset + 8); - s->sectors[i] += out_offset; - - /* sector count */ - s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); - - /* all-zeroes sector (type 2) does not need to be "uncompressed" and can - * therefore be unbounded. */ - if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { - error_report("sector count %" PRIu64 " for chunk %" PRIu32 - " is larger than max (%u)", - s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); - ret = -EINVAL; - goto fail; - } - - /* offset in (compressed) data fork */ - s->offsets[i] = buff_read_uint64(buffer, offset + 0x18); - s->offsets[i] += in_offset; - - /* length in (compressed) data fork */ - s->lengths[i] = buff_read_uint64(buffer, offset + 0x20); - - if (s->lengths[i] > DMG_LENGTHS_MAX) { - error_report("length %" PRIu64 " for chunk %" PRIu32 - " is larger than max (%u)", - s->lengths[i], i, DMG_LENGTHS_MAX); - ret = -EINVAL; - goto fail; - } - - update_max_chunk_size(s, i, &ds->max_compressed_size, - &ds->max_sectors_per_chunk); - offset += 40; - } - s->n_chunks += chunk_count; - return 0; - -fail: - return ret; -} - -static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, - uint64_t info_begin, uint64_t info_length) -{ - BDRVDMGState *s = bs->opaque; - int ret; - uint32_t count, rsrc_data_offset; - uint8_t *buffer = NULL; - uint64_t info_end; - uint64_t offset; - - /* read offset from begin of resource fork (info_begin) to resource data */ - ret = read_uint32(bs, info_begin, &rsrc_data_offset); - if (ret < 0) { - goto fail; - } else if (rsrc_data_offset > info_length) { - ret = -EINVAL; - goto fail; - } - - /* read length of resource data */ - ret = read_uint32(bs, info_begin + 8, &count); - if (ret < 0) { - goto fail; - } else if (count == 0 || rsrc_data_offset + count > info_length) { - ret = -EINVAL; - goto fail; - } - - /* begin of resource data (consisting of one or more resources) */ - offset = info_begin + rsrc_data_offset; - - /* end of resource data (there is possibly a following resource map - * which will be ignored). */ - info_end = offset + count; - - /* read offsets (mish blocks) from one or more resources in resource data */ - while (offset < info_end) { - /* size of following resource */ - ret = read_uint32(bs, offset, &count); - if (ret < 0) { - goto fail; - } else if (count == 0 || count > info_end - offset) { - ret = -EINVAL; - goto fail; - } - offset += 4; - - buffer = g_realloc(buffer, count); - ret = bdrv_pread(bs->file->bs, offset, buffer, count); - if (ret < 0) { - goto fail; - } - - ret = dmg_read_mish_block(s, ds, buffer, count); - if (ret < 0) { - goto fail; - } - /* advance offset by size of resource */ - offset += count; - } - ret = 0; - -fail: - g_free(buffer); - return ret; -} - -static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, - uint64_t info_begin, uint64_t info_length) -{ - BDRVDMGState *s = bs->opaque; - int ret; - uint8_t *buffer = NULL; - char *data_begin, *data_end; - - /* Have at least some length to avoid NULL for g_malloc. Attempt to set a - * safe upper cap on the data length. A test sample had a XML length of - * about 1 MiB. */ - if (info_length == 0 || info_length > 16 * 1024 * 1024) { - ret = -EINVAL; - goto fail; - } - - buffer = g_malloc(info_length + 1); - buffer[info_length] = '\0'; - ret = bdrv_pread(bs->file->bs, info_begin, buffer, info_length); - if (ret != info_length) { - ret = -EINVAL; - goto fail; - } - - /* look for <data>...</data>. The data is 284 (0x11c) bytes after base64 - * decode. The actual data element has 431 (0x1af) bytes which includes tabs - * and line feeds. */ - data_end = (char *)buffer; - while ((data_begin = strstr(data_end, "<data>")) != NULL) { - guchar *mish; - gsize out_len = 0; - - data_begin += 6; - data_end = strstr(data_begin, "</data>"); - /* malformed XML? */ - if (data_end == NULL) { - ret = -EINVAL; - goto fail; - } - *data_end++ = '\0'; - mish = g_base64_decode(data_begin, &out_len); - ret = dmg_read_mish_block(s, ds, mish, (uint32_t)out_len); - g_free(mish); - if (ret < 0) { - goto fail; - } - } - ret = 0; - -fail: - g_free(buffer); - return ret; -} - -static int dmg_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) -{ - BDRVDMGState *s = bs->opaque; - DmgHeaderState ds; - uint64_t rsrc_fork_offset, rsrc_fork_length; - uint64_t plist_xml_offset, plist_xml_length; - int64_t offset; - int ret; - - bs->read_only = 1; - s->n_chunks = 0; - s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; - /* used by dmg_read_mish_block to keep track of the current I/O position */ - ds.data_fork_offset = 0; - ds.max_compressed_size = 1; - ds.max_sectors_per_chunk = 1; - - /* locate the UDIF trailer */ - offset = dmg_find_koly_offset(bs->file->bs, errp); - if (offset < 0) { - ret = offset; - goto fail; - } - - /* offset of data fork (DataForkOffset) */ - ret = read_uint64(bs, offset + 0x18, &ds.data_fork_offset); - if (ret < 0) { - goto fail; - } else if (ds.data_fork_offset > offset) { - ret = -EINVAL; - goto fail; - } - - /* offset of resource fork (RsrcForkOffset) */ - ret = read_uint64(bs, offset + 0x28, &rsrc_fork_offset); - if (ret < 0) { - goto fail; - } - ret = read_uint64(bs, offset + 0x30, &rsrc_fork_length); - if (ret < 0) { - goto fail; - } - if (rsrc_fork_offset >= offset || - rsrc_fork_length > offset - rsrc_fork_offset) { - ret = -EINVAL; - goto fail; - } - /* offset of property list (XMLOffset) */ - ret = read_uint64(bs, offset + 0xd8, &plist_xml_offset); - if (ret < 0) { - goto fail; - } - ret = read_uint64(bs, offset + 0xe0, &plist_xml_length); - if (ret < 0) { - goto fail; - } - if (plist_xml_offset >= offset || - plist_xml_length > offset - plist_xml_offset) { - ret = -EINVAL; - goto fail; - } - ret = read_uint64(bs, offset + 0x1ec, (uint64_t *)&bs->total_sectors); - if (ret < 0) { - goto fail; - } - if (bs->total_sectors < 0) { - ret = -EINVAL; - goto fail; - } - if (rsrc_fork_length != 0) { - ret = dmg_read_resource_fork(bs, &ds, - rsrc_fork_offset, rsrc_fork_length); - if (ret < 0) { - goto fail; - } - } else if (plist_xml_length != 0) { - ret = dmg_read_plist_xml(bs, &ds, plist_xml_offset, plist_xml_length); - if (ret < 0) { - goto fail; - } - } else { - ret = -EINVAL; - goto fail; - } - - /* initialize zlib engine */ - s->compressed_chunk = qemu_try_blockalign(bs->file->bs, - ds.max_compressed_size + 1); - s->uncompressed_chunk = qemu_try_blockalign(bs->file->bs, - 512 * ds.max_sectors_per_chunk); - if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) { - ret = -ENOMEM; - goto fail; - } - - if (inflateInit(&s->zstream) != Z_OK) { - ret = -EINVAL; - goto fail; - } - - s->current_chunk = s->n_chunks; - - qemu_co_mutex_init(&s->lock); - return 0; - -fail: - g_free(s->types); - g_free(s->offsets); - g_free(s->lengths); - g_free(s->sectors); - g_free(s->sectorcounts); - qemu_vfree(s->compressed_chunk); - qemu_vfree(s->uncompressed_chunk); - return ret; -} - -static inline int is_sector_in_chunk(BDRVDMGState* s, - uint32_t chunk_num, uint64_t sector_num) -{ - if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num || - s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) { - return 0; - } else { - return -1; - } -} - -static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) -{ - /* binary search */ - uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; - while (chunk1 != chunk2) { - chunk3 = (chunk1 + chunk2) / 2; - if (s->sectors[chunk3] > sector_num) { - chunk2 = chunk3; - } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { - return chunk3; - } else { - chunk1 = chunk3; - } - } - return s->n_chunks; /* error */ -} - -static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) -{ - BDRVDMGState *s = bs->opaque; - - if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) { - int ret; - uint32_t chunk = search_chunk(s, sector_num); -#ifdef CONFIG_BZIP2 - uint64_t total_out; -#endif - - if (chunk >= s->n_chunks) { - return -1; - } - - s->current_chunk = s->n_chunks; - switch (s->types[chunk]) { /* block entry type */ - case 0x80000005: { /* zlib compressed */ - /* we need to buffer, because only the chunk as whole can be - * inflated. */ - ret = bdrv_pread(bs->file->bs, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { - return -1; - } - - s->zstream.next_in = s->compressed_chunk; - s->zstream.avail_in = s->lengths[chunk]; - s->zstream.next_out = s->uncompressed_chunk; - s->zstream.avail_out = 512 * s->sectorcounts[chunk]; - ret = inflateReset(&s->zstream); - if (ret != Z_OK) { - return -1; - } - ret = inflate(&s->zstream, Z_FINISH); - if (ret != Z_STREAM_END || - s->zstream.total_out != 512 * s->sectorcounts[chunk]) { - return -1; - } - break; } -#ifdef CONFIG_BZIP2 - case 0x80000006: /* bzip2 compressed */ - /* we need to buffer, because only the chunk as whole can be - * inflated. */ - ret = bdrv_pread(bs->file->bs, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { - return -1; - } - - ret = BZ2_bzDecompressInit(&s->bzstream, 0, 0); - if (ret != BZ_OK) { - return -1; - } - s->bzstream.next_in = (char *)s->compressed_chunk; - s->bzstream.avail_in = (unsigned int) s->lengths[chunk]; - s->bzstream.next_out = (char *)s->uncompressed_chunk; - s->bzstream.avail_out = (unsigned int) 512 * s->sectorcounts[chunk]; - ret = BZ2_bzDecompress(&s->bzstream); - total_out = ((uint64_t)s->bzstream.total_out_hi32 << 32) + - s->bzstream.total_out_lo32; - BZ2_bzDecompressEnd(&s->bzstream); - if (ret != BZ_STREAM_END || - total_out != 512 * s->sectorcounts[chunk]) { - return -1; - } - break; -#endif /* CONFIG_BZIP2 */ - case 1: /* copy */ - ret = bdrv_pread(bs->file->bs, s->offsets[chunk], - s->uncompressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { - return -1; - } - break; - case 2: /* zero */ - /* see dmg_read, it is treated specially. No buffer needs to be - * pre-filled, the zeroes can be set directly. */ - break; - } - s->current_chunk = chunk; - } - return 0; -} - -static int dmg_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - BDRVDMGState *s = bs->opaque; - int i; - - for (i = 0; i < nb_sectors; i++) { - uint32_t sector_offset_in_chunk; - if (dmg_read_chunk(bs, sector_num + i) != 0) { - return -1; - } - /* Special case: current chunk is all zeroes. Do not perform a memcpy as - * s->uncompressed_chunk may be too small to cover the large all-zeroes - * section. dmg_read_chunk is called to find s->current_chunk */ - if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ - memset(buf + i * 512, 0, 512); - continue; - } - sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk]; - memcpy(buf + i * 512, - s->uncompressed_chunk + sector_offset_in_chunk * 512, 512); - } - return 0; -} - -static coroutine_fn int dmg_co_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - int ret; - BDRVDMGState *s = bs->opaque; - qemu_co_mutex_lock(&s->lock); - ret = dmg_read(bs, sector_num, buf, nb_sectors); - qemu_co_mutex_unlock(&s->lock); - return ret; -} - -static void dmg_close(BlockDriverState *bs) -{ - BDRVDMGState *s = bs->opaque; - - g_free(s->types); - g_free(s->offsets); - g_free(s->lengths); - g_free(s->sectors); - g_free(s->sectorcounts); - qemu_vfree(s->compressed_chunk); - qemu_vfree(s->uncompressed_chunk); - - inflateEnd(&s->zstream); -} - -static BlockDriver bdrv_dmg = { - .format_name = "dmg", - .instance_size = sizeof(BDRVDMGState), - .bdrv_probe = dmg_probe, - .bdrv_open = dmg_open, - .bdrv_read = dmg_co_read, - .bdrv_close = dmg_close, -}; - -static void bdrv_dmg_init(void) -{ - bdrv_register(&bdrv_dmg); -} - -block_init(bdrv_dmg_init); |