@@ -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