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_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVDMGState *s = bs->opaque; uint64_t info_begin, info_end, last_in_offset, last_out_offset; uint32_t count, tmp; uint32_t max_compressed_size = 1, max_sectors_per_chunk = 1, i; int64_t offset; int ret; bs->read_only = 1; s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; /* read offset of info blocks */ offset = bdrv_getlength(bs->file); if (offset < 0) { ret = offset; goto fail; } offset -= 0x1d8; ret = read_uint64(bs, offset, &info_begin); if (ret < 0) { goto fail; } else if (info_begin == 0) { ret = -EINVAL; goto fail; } ret = read_uint32(bs, info_begin, &tmp); if (ret < 0) { goto fail; } else if (tmp != 0x100) { ret = -EINVAL; goto fail; } ret = read_uint32(bs, info_begin + 4, &count); if (ret < 0) { goto fail; } else if (count == 0) { ret = -EINVAL; goto fail; } info_end = info_begin + count; offset = info_begin + 0x100; /* read offsets */ last_in_offset = last_out_offset = 0; while (offset < info_end) { uint32_t type; ret = read_uint32(bs, offset, &count); if (ret < 0) { goto fail; } else if (count == 0) { ret = -EINVAL; goto fail; } offset += 4; ret = read_uint32(bs, offset, &type); if (ret < 0) { goto fail; } if (type == 0x6d697368 && count >= 244) { size_t new_size; uint32_t chunk_count; offset += 4; offset += 200; 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++) { ret = read_uint32(bs, offset, &s->types[i]); if (ret < 0) { goto fail; } offset += 4; if (s->types[i] != 0x80000005 && s->types[i] != 1 && s->types[i] != 2) { if (s->types[i] == 0xffffffff && i > 0) { last_in_offset = s->offsets[i - 1] + s->lengths[i - 1]; last_out_offset = s->sectors[i - 1] + s->sectorcounts[i - 1]; } chunk_count--; i--; offset += 36; continue; } offset += 4; ret = read_uint64(bs, offset, &s->sectors[i]); if (ret < 0) { goto fail; } s->sectors[i] += last_out_offset; offset += 8; ret = read_uint64(bs, offset, &s->sectorcounts[i]); if (ret < 0) { goto fail; } offset += 8; if (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; } ret = read_uint64(bs, offset, &s->offsets[i]); if (ret < 0) { goto fail; } s->offsets[i] += last_in_offset; offset += 8; ret = read_uint64(bs, offset, &s->lengths[i]); if (ret < 0) { goto fail; } offset += 8; 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, &max_compressed_size, &max_sectors_per_chunk); } s->n_chunks += chunk_count; } } /* initialize zlib engine */ s->compressed_chunk = qemu_try_blockalign(bs->file, max_compressed_size + 1); s->uncompressed_chunk = qemu_try_blockalign(bs->file, 512 * 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; }