From patchwork Fri Dec 18 16:06:29 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Richard W.M. Jones" X-Patchwork-Id: 41417 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DC90BB6F0A for ; Sat, 19 Dec 2009 03:13:45 +1100 (EST) Received: from localhost ([127.0.0.1]:47087 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NLfSX-0004ox-C3 for incoming@patchwork.ozlabs.org; Fri, 18 Dec 2009 11:13:41 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NLfLl-0007uO-QQ for qemu-devel@nongnu.org; Fri, 18 Dec 2009 11:06:41 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1NLfLh-0007qC-Se for qemu-devel@nongnu.org; Fri, 18 Dec 2009 11:06:41 -0500 Received: from [199.232.76.173] (port=44475 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NLfLh-0007q1-M1 for qemu-devel@nongnu.org; Fri, 18 Dec 2009 11:06:37 -0500 Received: from mx1.redhat.com ([209.132.183.28]:59909) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1NLfLg-0001ma-UX for qemu-devel@nongnu.org; Fri, 18 Dec 2009 11:06:37 -0500 Received: from int-mx05.intmail.prod.int.phx2.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.18]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id nBIG6aO7010857 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 18 Dec 2009 11:06:36 -0500 Received: from localhost (vpn2-10-80.ams2.redhat.com [10.36.10.80]) by int-mx05.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id nBIG6V4T008157 for ; Fri, 18 Dec 2009 11:06:33 -0500 Date: Fri, 18 Dec 2009 16:06:29 +0000 From: "Richard W.M. Jones" To: qemu-devel@nongnu.org Message-ID: <20091218160629.GA28180@amd.home.annexia.org> References: <20091218155655.GA28076@amd.home.annexia.org> <20091218155742.GA28141@amd.home.annexia.org> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20091218155742.GA28141@amd.home.annexia.org> User-Agent: Mutt/1.5.18 (2008-05-17) X-Scanned-By: MIMEDefang 2.67 on 10.5.11.18 X-detected-operating-system: by monty-python.gnu.org: Genre and OS details not recognized. Subject: [Qemu-devel] [PATCH 1/2] VMDK4: Parse the VMDK descriptor explicitly, improve handling of CIDs X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Let's fix a buffer overflow, and add the Signed-off-by line ... Rich. diff --git a/block/vmdk.c b/block/vmdk.c index 4e48622..0f59ea5 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -77,6 +77,10 @@ typedef struct BDRVVmdkState { unsigned int cluster_sectors; uint32_t parent_cid; int is_parent; + + /* VMDK4 fields */ + int64_t cid_offset; + int64_t parent_cid_offset; } BDRVVmdkState; typedef struct VmdkMetaData { @@ -112,57 +116,160 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) #define CHECK_CID 1 #define SECTOR_SIZE 512 -#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each #define HEADER_SIZE 512 // first sector of 512 bytes +/* Read and parse the VMDK4 descriptor. + * + * So-called "monolithic" VMDK4 files (which are the only ones we can + * currently read) have a text descriptor section embedded within the + * file at an offset described in the header. + * + * The other format for VMDK4 files ("split") has a toplevel .vmdk + * file which is the plain text descriptor, and that points to other + * files that contain the disk data. We can't read this sort of file + * yet. + */ +#define MAX_DESC_SIZE 100*512 + +#define VMDK4_ACCESS_RW 1 +#define VMDK4_ACCESS_RDONLY 2 +#define VMDK4_ACCESS_NOACCESS 3 + +static int vmdk4_parse_desc_extent(BlockDriverState *bs, VMDK4Header *header, + int access_type, char *p) +{ + /* We should probably check that the monolithic file + * has only one extent. + */ + return 0; +} + +static int vmdk4_parse_desc_assignment(BlockDriverState *bs, + VMDK4Header *header, char *p, size_t len, + int64_t file_offset) +{ + BDRVVmdkState *s = bs->opaque; + + if (strncmp(p, "version=", 8) == 0 && strcmp(p, "version=1") != 0) + fprintf (stderr, + "warning: vmdk4_parse_desc: descriptor version != 1 (%s)\n", + p); + + if (!strncmp(p, "CID=", 4)) + s->cid_offset = file_offset + 4; + + if (!strncmp(p, "parentCID=", 10)) + s->parent_cid_offset = file_offset + 10; + + return 0; +} + +static int vmdk4_parse_desc(BlockDriverState *bs, VMDK4Header *header) +{ + BDRVVmdkState *s = bs->opaque; + int64_t desc_offset, desc_size; + char desc[MAX_DESC_SIZE]; + char *p, *pnext; + size_t len; + + s->cid_offset = 0; + s->parent_cid_offset = 0; + + /* Stored in the header are the offset/size in sectors. */ + desc_size = le64_to_cpu(header->desc_size) * SECTOR_SIZE; + desc_offset = le64_to_cpu(header->desc_offset) * SECTOR_SIZE; + + if (desc_size > MAX_DESC_SIZE) { + fprintf (stderr, + "vmdk4_parse_desc: descriptor size > maximum size " + "(%" PRIi64 " > %d)\n", desc_size, MAX_DESC_SIZE); + return -1; + } + + if (bdrv_pread(s->hd, desc_offset, (uint8_t *)desc, desc_size) != desc_size) { + fprintf (stderr, + "vmdk4_parse_desc: could not read descriptor from " + "%" PRIi64 " size %" PRIi64 "\n", + desc_offset, desc_size); + return -1; + } + + if (!strncmp(desc, "# Disk DescriptorFile", 22)) { + fprintf (stderr, "vmdk4_parse_desc: unrecognized descriptor"); + return -1; + } + + /* Parse the descriptor line by line. */ + for (p = desc; p && *p; p = pnext) { + pnext = strchr (p, '\n'); + if (pnext != NULL) { + *pnext = '\0'; + pnext++; + } + + while (*p && isspace (*p)) + p++; + while ((len = strlen (p)) > 0 && isspace (p[len-1])) + p[len-1] = '\0'; + + if (!*p) + continue; + if (p[0] == '#') + continue; + + /* Ignore any lines that we can't parse. */ + if (!strncmp(p, "RW ", 3)) + vmdk4_parse_desc_extent (bs, header, VMDK4_ACCESS_RW, p + 3); + else if (!strncmp(p, "RDONLY ", 7)) + vmdk4_parse_desc_extent (bs, header, VMDK4_ACCESS_RDONLY, p + 7); + else if (!strncmp(p, "NOACCESS ", 9)) + vmdk4_parse_desc_extent (bs, header, VMDK4_ACCESS_NOACCESS, p + 9); + else /* else expecting name = value */ + vmdk4_parse_desc_assignment (bs, header, p, len, + p-desc+desc_offset); + } + + return 0; +} + static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) { BDRVVmdkState *s = bs->opaque; - char desc[DESC_SIZE]; + int64_t offset; + char str[9]; uint32_t cid; - const char *p_name, *cid_str; - size_t cid_str_size; - /* the descriptor offset = 0x200 */ - if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) - return 0; + if (parent) + offset = s->parent_cid_offset; + else + offset = s->cid_offset; - if (parent) { - cid_str = "parentCID"; - cid_str_size = sizeof("parentCID"); - } else { - cid_str = "CID"; - cid_str_size = sizeof("CID"); - } + if (offset == 0) + return 0; - if ((p_name = strstr(desc,cid_str)) != NULL) { - p_name += cid_str_size; - sscanf(p_name,"%x",&cid); - } + if (bdrv_pread(s->hd, offset, str, 8) != 8) + return 0; + str[8] = '\0'; + sscanf(str, "%x", &cid); return cid; } static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) { BDRVVmdkState *s = bs->opaque; - char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; - char *p_name, *tmp_str; + int64_t offset; + char str[9]; - /* the descriptor offset = 0x200 */ - if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) - return -1; + offset = s->cid_offset; + if (offset == 0) + return 0; - tmp_str = strstr(desc,"parentCID"); - pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); - if ((p_name = strstr(desc,"CID")) != NULL) { - p_name += sizeof("CID"); - snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid); - pstrcat(desc, sizeof(desc), tmp_desc); - } + snprintf(str, sizeof str, "%08x", cid); - if (bdrv_pwrite(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + if (bdrv_pwrite(s->hd, offset, str, 8) != 8) return -1; + return 0; } @@ -184,6 +291,8 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) return 1; } +#define DESC_SIZE 20*SECTOR_SIZE + static int vmdk_snapshot_create(const char *filename, const char *backing_file) { int snp_fd, p_fd; @@ -410,6 +519,9 @@ static int vmdk_open(BlockDriverState *bs, const char *filename, int flags) s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; + if (vmdk4_parse_desc(bs, &header) == -1) + goto fail; + if (parent_open) s->is_parent = 1; else