From patchwork Fri Jan 16 15:37:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 429899 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 5823B140277 for ; Sat, 17 Jan 2015 02:40:51 +1100 (AEDT) Received: from localhost ([::1]:56211 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YC90y-0006i5-6W for incoming@patchwork.ozlabs.org; Fri, 16 Jan 2015 10:40:48 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49838) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YC8xp-0001Kt-I3 for qemu-devel@nongnu.org; Fri, 16 Jan 2015 10:37:38 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YC8xk-0000fU-JO for qemu-devel@nongnu.org; Fri, 16 Jan 2015 10:37:33 -0500 Received: from mx1.redhat.com ([209.132.183.28]:57685) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YC8xk-0000fM-9s for qemu-devel@nongnu.org; Fri, 16 Jan 2015 10:37:28 -0500 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id t0GFbMVS022669 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 16 Jan 2015 10:37:22 -0500 Received: from localhost (ovpn-112-60.ams2.redhat.com [10.36.112.60]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t0GFbLJA027276; Fri, 16 Jan 2015 10:37:21 -0500 From: Stefan Hajnoczi To: Date: Fri, 16 Jan 2015 15:37:00 +0000 Message-Id: <1421422633-25536-4-git-send-email-stefanha@redhat.com> In-Reply-To: <1421422633-25536-1-git-send-email-stefanha@redhat.com> References: <1421422633-25536-1-git-send-email-stefanha@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: Peter Maydell , Peter Wu , Stefan Hajnoczi Subject: [Qemu-devel] [PULL 03/16] block/dmg: extract mish block decoding functionality X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Peter Wu Extract the mish block decoder such that this can be used for other formats in the future. A new DmgHeaderState struct is introduced to share state while decoding. The code is kept unchanged as much as possible, a "fail" label is added for example where a simple return would probably do. In dmg_open, the variable "tmp" is renamed to "rsrc_data_offset" for clarity and comments have been added explaining various data. Note that this patch has one subtle difference with the previous version which should not affect functionality. In the previous code, the end of a resource was inferred from the mish block (the offsets would be increased by the fields). In this patch, the resource length is used instead to avoid the need to rely on the previous offsets. Signed-off-by: Peter Wu Reviewed-by: John Snow Reviewed-by: Stefan Hajnoczi Message-id: 1420566495-13284-3-git-send-email-peter@lekensteyn.nl Signed-off-by: Stefan Hajnoczi --- block/dmg.c | 228 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 133 insertions(+), 95 deletions(-) diff --git a/block/dmg.c b/block/dmg.c index cdad28f..c571ac9 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -171,19 +171,138 @@ static int64_t dmg_find_koly_offset(BlockDriverState *file_bs, Error **errp) 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 last_in_offset; + uint64_t last_out_offset; + /* exported for dmg_open */ + uint32_t max_compressed_size; + uint32_t max_sectors_per_chunk; +} DmgHeaderState; + +static int dmg_read_mish_block(BlockDriverState *bs, DmgHeaderState *ds, + int64_t offset, uint32_t count) +{ + BDRVDMGState *s = bs->opaque; + uint32_t type, i; + int ret; + size_t new_size; + uint32_t chunk_count; + + ret = read_uint32(bs, offset, &type); + if (ret < 0) { + goto fail; + } + + /* 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; + } + + 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) { + ds->last_in_offset = s->offsets[i - 1] + s->lengths[i - 1]; + ds->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] += ds->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] += ds->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, &ds->max_compressed_size, + &ds->max_sectors_per_chunk); + } + 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; + DmgHeaderState ds; + uint64_t info_begin, info_end; + uint32_t count, rsrc_data_offset; 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.last_in_offset = 0; + ds.last_out_offset = 0; + ds.max_compressed_size = 1; + ds.max_sectors_per_chunk = 1; /* locate the UDIF trailer */ offset = dmg_find_koly_offset(bs->file, errp); @@ -200,10 +319,10 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = read_uint32(bs, info_begin, &tmp); + ret = read_uint32(bs, info_begin, &rsrc_data_offset); if (ret < 0) { goto fail; - } else if (tmp != 0x100) { + } else if (rsrc_data_offset != 0x100) { ret = -EINVAL; goto fail; } @@ -215,15 +334,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } + /* end of resource data, ignoring the following resource map */ info_end = info_begin + count; + /* begin of resource data (consisting of one or more resources) */ offset = info_begin + 0x100; - /* read offsets */ - last_in_offset = last_out_offset = 0; + /* read offsets (mish blocks) from one or more resources in resource data */ while (offset < info_end) { - uint32_t type; - + /* size of following resource */ ret = read_uint32(bs, offset, &count); if (ret < 0) { goto fail; @@ -233,100 +352,19 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, } offset += 4; - ret = read_uint32(bs, offset, &type); + ret = dmg_read_mish_block(bs, &ds, offset, count); 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; - } + /* advance offset by size of resource */ + offset += count; } /* initialize zlib engine */ s->compressed_chunk = qemu_try_blockalign(bs->file, - max_compressed_size + 1); + ds.max_compressed_size + 1); s->uncompressed_chunk = qemu_try_blockalign(bs->file, - 512 * max_sectors_per_chunk); + 512 * ds.max_sectors_per_chunk); if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) { ret = -ENOMEM; goto fail;