diff mbox

[1/2] VMDK4: Parse the VMDK descriptor explicitly, improve handling of CIDs

Message ID 20091218160629.GA28180@amd.home.annexia.org
State New
Headers show

Commit Message

Richard W.M. Jones Dec. 18, 2009, 4:06 p.m. UTC
Let's fix a buffer overflow, and add the Signed-off-by line ...

Rich.
diff mbox

Patch

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