From patchwork Fri Jan 16 15:36:59 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 429896 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 317D41401EB for ; Sat, 17 Jan 2015 02:39:42 +1100 (AEDT) Received: from localhost ([::1]:56198 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YC8zs-0004v6-6n for incoming@patchwork.ozlabs.org; Fri, 16 Jan 2015 10:39:40 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49826) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YC8xm-0001EN-Ve for qemu-devel@nongnu.org; Fri, 16 Jan 2015 10:37:34 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YC8xi-0000fB-Tk for qemu-devel@nongnu.org; Fri, 16 Jan 2015 10:37:30 -0500 Received: from mx1.redhat.com ([209.132.183.28]:44601) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YC8xi-0000et-1H for qemu-devel@nongnu.org; Fri, 16 Jan 2015 10:37:26 -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 t0GFbKr4000583 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 16 Jan 2015 10:37:20 -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 t0GFbIGh027268; Fri, 16 Jan 2015 10:37:19 -0500 From: Stefan Hajnoczi To: Date: Fri, 16 Jan 2015 15:36:59 +0000 Message-Id: <1421422633-25536-3-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 02/16] block/dmg: properly detect the UDIF trailer 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 DMG files have a variable length with a UDIF trailer at the end of a file. This UDIF trailer is essential as it describes the contents of the image. At the moment however, the start of this trailer is almost always incorrect as bdrv_getlength() returns a multiple of the block size (rounded up). This results in a failure to recognize DMG files, resulting in Invalid argument (EINVAL) errors. As there is no API to retrieve the real file size, look for the magic header in the last two sectors to find the start of this 512-byte UDIF trailer (the "koly" block). The resource fork offset ("info_begin") has its offset adjusted as the initial value of offset does not mean "end of file" anymore, but "begin of UDIF trailer". [Replaced error_set(errp, ERROR_CLASS_GENERIC_ERROR, ...) with error_setg(errp, ...) as discussed with Peter. --Stefan] Signed-off-by: Peter Wu Reviewed-by: John Snow Reviewed-by: Stefan Hajnoczi Message-id: 1420566495-13284-2-git-send-email-peter@lekensteyn.nl Signed-off-by: Stefan Hajnoczi --- block/dmg.c | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/block/dmg.c b/block/dmg.c index e455886..cdad28f 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -131,6 +131,46 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, } } +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; +} + static int dmg_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -145,15 +185,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; - /* read offset of info blocks */ - offset = bdrv_getlength(bs->file); + /* locate the UDIF trailer */ + offset = dmg_find_koly_offset(bs->file, errp); if (offset < 0) { ret = offset; goto fail; } - offset -= 0x1d8; - ret = read_uint64(bs, offset, &info_begin); + ret = read_uint64(bs, offset + 0x28, &info_begin); if (ret < 0) { goto fail; } else if (info_begin == 0) {