1019d6b8fSAnthony Liguori /* 2019d6b8fSAnthony Liguori * Block driver for the VMDK format 3019d6b8fSAnthony Liguori * 4019d6b8fSAnthony Liguori * Copyright (c) 2004 Fabrice Bellard 5019d6b8fSAnthony Liguori * Copyright (c) 2005 Filip Navara 6019d6b8fSAnthony Liguori * 7019d6b8fSAnthony Liguori * Permission is hereby granted, free of charge, to any person obtaining a copy 8019d6b8fSAnthony Liguori * of this software and associated documentation files (the "Software"), to deal 9019d6b8fSAnthony Liguori * in the Software without restriction, including without limitation the rights 10019d6b8fSAnthony Liguori * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11019d6b8fSAnthony Liguori * copies of the Software, and to permit persons to whom the Software is 12019d6b8fSAnthony Liguori * furnished to do so, subject to the following conditions: 13019d6b8fSAnthony Liguori * 14019d6b8fSAnthony Liguori * The above copyright notice and this permission notice shall be included in 15019d6b8fSAnthony Liguori * all copies or substantial portions of the Software. 16019d6b8fSAnthony Liguori * 17019d6b8fSAnthony Liguori * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18019d6b8fSAnthony Liguori * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19019d6b8fSAnthony Liguori * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20019d6b8fSAnthony Liguori * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21019d6b8fSAnthony Liguori * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22019d6b8fSAnthony Liguori * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23019d6b8fSAnthony Liguori * THE SOFTWARE. 24019d6b8fSAnthony Liguori */ 25019d6b8fSAnthony Liguori 26019d6b8fSAnthony Liguori #include "qemu-common.h" 27019d6b8fSAnthony Liguori #include "block_int.h" 28019d6b8fSAnthony Liguori #include "module.h" 29019d6b8fSAnthony Liguori 30019d6b8fSAnthony Liguori #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') 31019d6b8fSAnthony Liguori #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') 32019d6b8fSAnthony Liguori 33019d6b8fSAnthony Liguori typedef struct { 34019d6b8fSAnthony Liguori uint32_t version; 35019d6b8fSAnthony Liguori uint32_t flags; 36019d6b8fSAnthony Liguori uint32_t disk_sectors; 37019d6b8fSAnthony Liguori uint32_t granularity; 38019d6b8fSAnthony Liguori uint32_t l1dir_offset; 39019d6b8fSAnthony Liguori uint32_t l1dir_size; 40019d6b8fSAnthony Liguori uint32_t file_sectors; 41019d6b8fSAnthony Liguori uint32_t cylinders; 42019d6b8fSAnthony Liguori uint32_t heads; 43019d6b8fSAnthony Liguori uint32_t sectors_per_track; 44019d6b8fSAnthony Liguori } VMDK3Header; 45019d6b8fSAnthony Liguori 46019d6b8fSAnthony Liguori typedef struct { 47019d6b8fSAnthony Liguori uint32_t version; 48019d6b8fSAnthony Liguori uint32_t flags; 49019d6b8fSAnthony Liguori int64_t capacity; 50019d6b8fSAnthony Liguori int64_t granularity; 51019d6b8fSAnthony Liguori int64_t desc_offset; 52019d6b8fSAnthony Liguori int64_t desc_size; 53019d6b8fSAnthony Liguori int32_t num_gtes_per_gte; 54019d6b8fSAnthony Liguori int64_t rgd_offset; 55019d6b8fSAnthony Liguori int64_t gd_offset; 56019d6b8fSAnthony Liguori int64_t grain_offset; 57019d6b8fSAnthony Liguori char filler[1]; 58019d6b8fSAnthony Liguori char check_bytes[4]; 59019d6b8fSAnthony Liguori } __attribute__((packed)) VMDK4Header; 60019d6b8fSAnthony Liguori 61019d6b8fSAnthony Liguori #define L2_CACHE_SIZE 16 62019d6b8fSAnthony Liguori 63b3976d3cSFam Zheng typedef struct VmdkExtent { 64b3976d3cSFam Zheng BlockDriverState *file; 65b3976d3cSFam Zheng bool flat; 66b3976d3cSFam Zheng int64_t sectors; 67b3976d3cSFam Zheng int64_t end_sector; 687fa60fa3SFam Zheng int64_t flat_start_offset; 69019d6b8fSAnthony Liguori int64_t l1_table_offset; 70019d6b8fSAnthony Liguori int64_t l1_backup_table_offset; 71019d6b8fSAnthony Liguori uint32_t *l1_table; 72019d6b8fSAnthony Liguori uint32_t *l1_backup_table; 73019d6b8fSAnthony Liguori unsigned int l1_size; 74019d6b8fSAnthony Liguori uint32_t l1_entry_sectors; 75019d6b8fSAnthony Liguori 76019d6b8fSAnthony Liguori unsigned int l2_size; 77019d6b8fSAnthony Liguori uint32_t *l2_cache; 78019d6b8fSAnthony Liguori uint32_t l2_cache_offsets[L2_CACHE_SIZE]; 79019d6b8fSAnthony Liguori uint32_t l2_cache_counts[L2_CACHE_SIZE]; 80019d6b8fSAnthony Liguori 81019d6b8fSAnthony Liguori unsigned int cluster_sectors; 82b3976d3cSFam Zheng } VmdkExtent; 83b3976d3cSFam Zheng 84b3976d3cSFam Zheng typedef struct BDRVVmdkState { 85e1da9b24SFam Zheng int desc_offset; 8669b4d86dSFam Zheng bool cid_updated; 87019d6b8fSAnthony Liguori uint32_t parent_cid; 88b3976d3cSFam Zheng int num_extents; 89b3976d3cSFam Zheng /* Extent array with num_extents entries, ascend ordered by address */ 90b3976d3cSFam Zheng VmdkExtent *extents; 91019d6b8fSAnthony Liguori } BDRVVmdkState; 92019d6b8fSAnthony Liguori 93019d6b8fSAnthony Liguori typedef struct VmdkMetaData { 94019d6b8fSAnthony Liguori uint32_t offset; 95019d6b8fSAnthony Liguori unsigned int l1_index; 96019d6b8fSAnthony Liguori unsigned int l2_index; 97019d6b8fSAnthony Liguori unsigned int l2_offset; 98019d6b8fSAnthony Liguori int valid; 99019d6b8fSAnthony Liguori } VmdkMetaData; 100019d6b8fSAnthony Liguori 101019d6b8fSAnthony Liguori static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) 102019d6b8fSAnthony Liguori { 103019d6b8fSAnthony Liguori uint32_t magic; 104019d6b8fSAnthony Liguori 105019d6b8fSAnthony Liguori if (buf_size < 4) 106019d6b8fSAnthony Liguori return 0; 107019d6b8fSAnthony Liguori magic = be32_to_cpu(*(uint32_t *)buf); 108019d6b8fSAnthony Liguori if (magic == VMDK3_MAGIC || 10901fc99d6SFam Zheng magic == VMDK4_MAGIC) { 110019d6b8fSAnthony Liguori return 100; 11101fc99d6SFam Zheng } else { 11201fc99d6SFam Zheng const char *p = (const char *)buf; 11301fc99d6SFam Zheng const char *end = p + buf_size; 11401fc99d6SFam Zheng while (p < end) { 11501fc99d6SFam Zheng if (*p == '#') { 11601fc99d6SFam Zheng /* skip comment line */ 11701fc99d6SFam Zheng while (p < end && *p != '\n') { 11801fc99d6SFam Zheng p++; 11901fc99d6SFam Zheng } 12001fc99d6SFam Zheng p++; 12101fc99d6SFam Zheng continue; 12201fc99d6SFam Zheng } 12301fc99d6SFam Zheng if (*p == ' ') { 12401fc99d6SFam Zheng while (p < end && *p == ' ') { 12501fc99d6SFam Zheng p++; 12601fc99d6SFam Zheng } 12701fc99d6SFam Zheng /* skip '\r' if windows line endings used. */ 12801fc99d6SFam Zheng if (p < end && *p == '\r') { 12901fc99d6SFam Zheng p++; 13001fc99d6SFam Zheng } 13101fc99d6SFam Zheng /* only accept blank lines before 'version=' line */ 13201fc99d6SFam Zheng if (p == end || *p != '\n') { 133019d6b8fSAnthony Liguori return 0; 134019d6b8fSAnthony Liguori } 13501fc99d6SFam Zheng p++; 13601fc99d6SFam Zheng continue; 13701fc99d6SFam Zheng } 13801fc99d6SFam Zheng if (end - p >= strlen("version=X\n")) { 13901fc99d6SFam Zheng if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 || 14001fc99d6SFam Zheng strncmp("version=2\n", p, strlen("version=2\n")) == 0) { 14101fc99d6SFam Zheng return 100; 14201fc99d6SFam Zheng } 14301fc99d6SFam Zheng } 14401fc99d6SFam Zheng if (end - p >= strlen("version=X\r\n")) { 14501fc99d6SFam Zheng if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 || 14601fc99d6SFam Zheng strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) { 14701fc99d6SFam Zheng return 100; 14801fc99d6SFam Zheng } 14901fc99d6SFam Zheng } 15001fc99d6SFam Zheng return 0; 15101fc99d6SFam Zheng } 15201fc99d6SFam Zheng return 0; 15301fc99d6SFam Zheng } 15401fc99d6SFam Zheng } 155019d6b8fSAnthony Liguori 156019d6b8fSAnthony Liguori #define CHECK_CID 1 157019d6b8fSAnthony Liguori 158019d6b8fSAnthony Liguori #define SECTOR_SIZE 512 159*f66fd6c3SFam Zheng #define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */ 160*f66fd6c3SFam Zheng #define BUF_SIZE 4096 161*f66fd6c3SFam Zheng #define HEADER_SIZE 512 /* first sector of 512 bytes */ 162019d6b8fSAnthony Liguori 163b3976d3cSFam Zheng static void vmdk_free_extents(BlockDriverState *bs) 164b3976d3cSFam Zheng { 165b3976d3cSFam Zheng int i; 166b3976d3cSFam Zheng BDRVVmdkState *s = bs->opaque; 167b3976d3cSFam Zheng 168b3976d3cSFam Zheng for (i = 0; i < s->num_extents; i++) { 169b3976d3cSFam Zheng qemu_free(s->extents[i].l1_table); 170b3976d3cSFam Zheng qemu_free(s->extents[i].l2_cache); 171b3976d3cSFam Zheng qemu_free(s->extents[i].l1_backup_table); 172b3976d3cSFam Zheng } 173b3976d3cSFam Zheng qemu_free(s->extents); 174b3976d3cSFam Zheng } 175b3976d3cSFam Zheng 176019d6b8fSAnthony Liguori static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) 177019d6b8fSAnthony Liguori { 178019d6b8fSAnthony Liguori char desc[DESC_SIZE]; 179019d6b8fSAnthony Liguori uint32_t cid; 180019d6b8fSAnthony Liguori const char *p_name, *cid_str; 181019d6b8fSAnthony Liguori size_t cid_str_size; 182e1da9b24SFam Zheng BDRVVmdkState *s = bs->opaque; 183019d6b8fSAnthony Liguori 184e1da9b24SFam Zheng if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { 185019d6b8fSAnthony Liguori return 0; 186e1da9b24SFam Zheng } 187019d6b8fSAnthony Liguori 188019d6b8fSAnthony Liguori if (parent) { 189019d6b8fSAnthony Liguori cid_str = "parentCID"; 190019d6b8fSAnthony Liguori cid_str_size = sizeof("parentCID"); 191019d6b8fSAnthony Liguori } else { 192019d6b8fSAnthony Liguori cid_str = "CID"; 193019d6b8fSAnthony Liguori cid_str_size = sizeof("CID"); 194019d6b8fSAnthony Liguori } 195019d6b8fSAnthony Liguori 196019d6b8fSAnthony Liguori if ((p_name = strstr(desc,cid_str)) != NULL) { 197019d6b8fSAnthony Liguori p_name += cid_str_size; 198019d6b8fSAnthony Liguori sscanf(p_name,"%x",&cid); 199019d6b8fSAnthony Liguori } 200019d6b8fSAnthony Liguori 201019d6b8fSAnthony Liguori return cid; 202019d6b8fSAnthony Liguori } 203019d6b8fSAnthony Liguori 204019d6b8fSAnthony Liguori static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) 205019d6b8fSAnthony Liguori { 206019d6b8fSAnthony Liguori char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; 207019d6b8fSAnthony Liguori char *p_name, *tmp_str; 208e1da9b24SFam Zheng BDRVVmdkState *s = bs->opaque; 209019d6b8fSAnthony Liguori 210e1da9b24SFam Zheng memset(desc, 0, sizeof(desc)); 211e1da9b24SFam Zheng if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { 212e1da9b24SFam Zheng return -EIO; 213e1da9b24SFam Zheng } 214019d6b8fSAnthony Liguori 215019d6b8fSAnthony Liguori tmp_str = strstr(desc,"parentCID"); 216019d6b8fSAnthony Liguori pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); 217019d6b8fSAnthony Liguori if ((p_name = strstr(desc,"CID")) != NULL) { 218019d6b8fSAnthony Liguori p_name += sizeof("CID"); 219019d6b8fSAnthony Liguori snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid); 220019d6b8fSAnthony Liguori pstrcat(desc, sizeof(desc), tmp_desc); 221019d6b8fSAnthony Liguori } 222019d6b8fSAnthony Liguori 223e1da9b24SFam Zheng if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) { 224e1da9b24SFam Zheng return -EIO; 225e1da9b24SFam Zheng } 226019d6b8fSAnthony Liguori return 0; 227019d6b8fSAnthony Liguori } 228019d6b8fSAnthony Liguori 229019d6b8fSAnthony Liguori static int vmdk_is_cid_valid(BlockDriverState *bs) 230019d6b8fSAnthony Liguori { 231019d6b8fSAnthony Liguori #ifdef CHECK_CID 232019d6b8fSAnthony Liguori BDRVVmdkState *s = bs->opaque; 233b171271aSKevin Wolf BlockDriverState *p_bs = bs->backing_hd; 234019d6b8fSAnthony Liguori uint32_t cur_pcid; 235019d6b8fSAnthony Liguori 236019d6b8fSAnthony Liguori if (p_bs) { 237019d6b8fSAnthony Liguori cur_pcid = vmdk_read_cid(p_bs,0); 238019d6b8fSAnthony Liguori if (s->parent_cid != cur_pcid) 239019d6b8fSAnthony Liguori // CID not valid 240019d6b8fSAnthony Liguori return 0; 241019d6b8fSAnthony Liguori } 242019d6b8fSAnthony Liguori #endif 243019d6b8fSAnthony Liguori // CID valid 244019d6b8fSAnthony Liguori return 1; 245019d6b8fSAnthony Liguori } 246019d6b8fSAnthony Liguori 2479949f97eSKevin Wolf static int vmdk_parent_open(BlockDriverState *bs) 248019d6b8fSAnthony Liguori { 249019d6b8fSAnthony Liguori char *p_name; 2507fa60fa3SFam Zheng char desc[DESC_SIZE + 1]; 251e1da9b24SFam Zheng BDRVVmdkState *s = bs->opaque; 252019d6b8fSAnthony Liguori 2537fa60fa3SFam Zheng desc[DESC_SIZE] = '\0'; 254e1da9b24SFam Zheng if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { 255019d6b8fSAnthony Liguori return -1; 256e1da9b24SFam Zheng } 257019d6b8fSAnthony Liguori 258019d6b8fSAnthony Liguori if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { 259019d6b8fSAnthony Liguori char *end_name; 260019d6b8fSAnthony Liguori 261019d6b8fSAnthony Liguori p_name += sizeof("parentFileNameHint") + 1; 262019d6b8fSAnthony Liguori if ((end_name = strchr(p_name,'\"')) == NULL) 263019d6b8fSAnthony Liguori return -1; 264b171271aSKevin Wolf if ((end_name - p_name) > sizeof (bs->backing_file) - 1) 265019d6b8fSAnthony Liguori return -1; 266019d6b8fSAnthony Liguori 267b171271aSKevin Wolf pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); 268019d6b8fSAnthony Liguori } 269019d6b8fSAnthony Liguori 270019d6b8fSAnthony Liguori return 0; 271019d6b8fSAnthony Liguori } 272019d6b8fSAnthony Liguori 273b3976d3cSFam Zheng /* Create and append extent to the extent array. Return the added VmdkExtent 274b3976d3cSFam Zheng * address. return NULL if allocation failed. */ 275b3976d3cSFam Zheng static VmdkExtent *vmdk_add_extent(BlockDriverState *bs, 276b3976d3cSFam Zheng BlockDriverState *file, bool flat, int64_t sectors, 277b3976d3cSFam Zheng int64_t l1_offset, int64_t l1_backup_offset, 278b3976d3cSFam Zheng uint32_t l1_size, 279b3976d3cSFam Zheng int l2_size, unsigned int cluster_sectors) 280b3976d3cSFam Zheng { 281b3976d3cSFam Zheng VmdkExtent *extent; 282b3976d3cSFam Zheng BDRVVmdkState *s = bs->opaque; 283b3976d3cSFam Zheng 284b3976d3cSFam Zheng s->extents = qemu_realloc(s->extents, 285b3976d3cSFam Zheng (s->num_extents + 1) * sizeof(VmdkExtent)); 286b3976d3cSFam Zheng extent = &s->extents[s->num_extents]; 287b3976d3cSFam Zheng s->num_extents++; 288b3976d3cSFam Zheng 289b3976d3cSFam Zheng memset(extent, 0, sizeof(VmdkExtent)); 290b3976d3cSFam Zheng extent->file = file; 291b3976d3cSFam Zheng extent->flat = flat; 292b3976d3cSFam Zheng extent->sectors = sectors; 293b3976d3cSFam Zheng extent->l1_table_offset = l1_offset; 294b3976d3cSFam Zheng extent->l1_backup_table_offset = l1_backup_offset; 295b3976d3cSFam Zheng extent->l1_size = l1_size; 296b3976d3cSFam Zheng extent->l1_entry_sectors = l2_size * cluster_sectors; 297b3976d3cSFam Zheng extent->l2_size = l2_size; 298b3976d3cSFam Zheng extent->cluster_sectors = cluster_sectors; 299b3976d3cSFam Zheng 300b3976d3cSFam Zheng if (s->num_extents > 1) { 301b3976d3cSFam Zheng extent->end_sector = (*(extent - 1)).end_sector + extent->sectors; 302b3976d3cSFam Zheng } else { 303b3976d3cSFam Zheng extent->end_sector = extent->sectors; 304b3976d3cSFam Zheng } 305b3976d3cSFam Zheng bs->total_sectors = extent->end_sector; 306b3976d3cSFam Zheng return extent; 307b3976d3cSFam Zheng } 308b3976d3cSFam Zheng 309b4b3ab14SFam Zheng static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent) 310019d6b8fSAnthony Liguori { 311b4b3ab14SFam Zheng int ret; 312b4b3ab14SFam Zheng int l1_size, i; 313b4b3ab14SFam Zheng 314b4b3ab14SFam Zheng /* read the L1 table */ 315b4b3ab14SFam Zheng l1_size = extent->l1_size * sizeof(uint32_t); 316b4b3ab14SFam Zheng extent->l1_table = qemu_malloc(l1_size); 317b4b3ab14SFam Zheng ret = bdrv_pread(extent->file, 318b4b3ab14SFam Zheng extent->l1_table_offset, 319b4b3ab14SFam Zheng extent->l1_table, 320b4b3ab14SFam Zheng l1_size); 321b4b3ab14SFam Zheng if (ret < 0) { 322b4b3ab14SFam Zheng goto fail_l1; 323b4b3ab14SFam Zheng } 324b4b3ab14SFam Zheng for (i = 0; i < extent->l1_size; i++) { 325b4b3ab14SFam Zheng le32_to_cpus(&extent->l1_table[i]); 326b4b3ab14SFam Zheng } 327b4b3ab14SFam Zheng 328b4b3ab14SFam Zheng if (extent->l1_backup_table_offset) { 329b4b3ab14SFam Zheng extent->l1_backup_table = qemu_malloc(l1_size); 330b4b3ab14SFam Zheng ret = bdrv_pread(extent->file, 331b4b3ab14SFam Zheng extent->l1_backup_table_offset, 332b4b3ab14SFam Zheng extent->l1_backup_table, 333b4b3ab14SFam Zheng l1_size); 334b4b3ab14SFam Zheng if (ret < 0) { 335b4b3ab14SFam Zheng goto fail_l1b; 336b4b3ab14SFam Zheng } 337b4b3ab14SFam Zheng for (i = 0; i < extent->l1_size; i++) { 338b4b3ab14SFam Zheng le32_to_cpus(&extent->l1_backup_table[i]); 339b4b3ab14SFam Zheng } 340b4b3ab14SFam Zheng } 341b4b3ab14SFam Zheng 342b4b3ab14SFam Zheng extent->l2_cache = 343b4b3ab14SFam Zheng qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); 344b4b3ab14SFam Zheng return 0; 345b4b3ab14SFam Zheng fail_l1b: 346b4b3ab14SFam Zheng qemu_free(extent->l1_backup_table); 347b4b3ab14SFam Zheng fail_l1: 348b4b3ab14SFam Zheng qemu_free(extent->l1_table); 349b4b3ab14SFam Zheng return ret; 350b4b3ab14SFam Zheng } 351b4b3ab14SFam Zheng 352b4b3ab14SFam Zheng static int vmdk_open_vmdk3(BlockDriverState *bs, int flags) 353b4b3ab14SFam Zheng { 354b4b3ab14SFam Zheng int ret; 355019d6b8fSAnthony Liguori uint32_t magic; 356019d6b8fSAnthony Liguori VMDK3Header header; 357e1da9b24SFam Zheng BDRVVmdkState *s = bs->opaque; 358b4b3ab14SFam Zheng VmdkExtent *extent; 359b4b3ab14SFam Zheng 360e1da9b24SFam Zheng s->desc_offset = 0x200; 361b4b3ab14SFam Zheng ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); 362b4b3ab14SFam Zheng if (ret < 0) { 363019d6b8fSAnthony Liguori goto fail; 364b3976d3cSFam Zheng } 365b4b3ab14SFam Zheng extent = vmdk_add_extent(bs, 366b4b3ab14SFam Zheng bs->file, false, 367b3976d3cSFam Zheng le32_to_cpu(header.disk_sectors), 368b4b3ab14SFam Zheng le32_to_cpu(header.l1dir_offset) << 9, 369b4b3ab14SFam Zheng 0, 1 << 6, 1 << 9, 370b4b3ab14SFam Zheng le32_to_cpu(header.granularity)); 371b4b3ab14SFam Zheng ret = vmdk_init_tables(bs, extent); 372b4b3ab14SFam Zheng if (ret) { 373b4b3ab14SFam Zheng /* vmdk_init_tables cleans up on fail, so only free allocation of 374b4b3ab14SFam Zheng * vmdk_add_extent here. */ 375b4b3ab14SFam Zheng goto fail; 376b4b3ab14SFam Zheng } 377b4b3ab14SFam Zheng return 0; 378b4b3ab14SFam Zheng fail: 379b4b3ab14SFam Zheng vmdk_free_extents(bs); 380b4b3ab14SFam Zheng return ret; 381b4b3ab14SFam Zheng } 382b4b3ab14SFam Zheng 383b4b3ab14SFam Zheng static int vmdk_open_vmdk4(BlockDriverState *bs, int flags) 384b4b3ab14SFam Zheng { 385b4b3ab14SFam Zheng int ret; 386b4b3ab14SFam Zheng uint32_t magic; 387b4b3ab14SFam Zheng uint32_t l1_size, l1_entry_sectors; 388019d6b8fSAnthony Liguori VMDK4Header header; 389b4b3ab14SFam Zheng BDRVVmdkState *s = bs->opaque; 390b4b3ab14SFam Zheng VmdkExtent *extent; 391b4b3ab14SFam Zheng 392e1da9b24SFam Zheng s->desc_offset = 0x200; 393b4b3ab14SFam Zheng ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); 394b4b3ab14SFam Zheng if (ret < 0) { 395019d6b8fSAnthony Liguori goto fail; 396b3976d3cSFam Zheng } 397b3976d3cSFam Zheng l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) 398b3976d3cSFam Zheng * le64_to_cpu(header.granularity); 399b3976d3cSFam Zheng l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) 400b3976d3cSFam Zheng / l1_entry_sectors; 401b3976d3cSFam Zheng extent = vmdk_add_extent(bs, bs->file, false, 402b3976d3cSFam Zheng le64_to_cpu(header.capacity), 403b3976d3cSFam Zheng le64_to_cpu(header.gd_offset) << 9, 404b3976d3cSFam Zheng le64_to_cpu(header.rgd_offset) << 9, 405b3976d3cSFam Zheng l1_size, 406b3976d3cSFam Zheng le32_to_cpu(header.num_gtes_per_gte), 407b3976d3cSFam Zheng le64_to_cpu(header.granularity)); 408b3976d3cSFam Zheng if (extent->l1_entry_sectors <= 0) { 409b4b3ab14SFam Zheng ret = -EINVAL; 410019d6b8fSAnthony Liguori goto fail; 411b3976d3cSFam Zheng } 412b4b3ab14SFam Zheng /* try to open parent images, if exist */ 413b4b3ab14SFam Zheng ret = vmdk_parent_open(bs); 414b4b3ab14SFam Zheng if (ret) { 415019d6b8fSAnthony Liguori goto fail; 416b4b3ab14SFam Zheng } 417019d6b8fSAnthony Liguori s->parent_cid = vmdk_read_cid(bs, 1); 418b4b3ab14SFam Zheng ret = vmdk_init_tables(bs, extent); 419b4b3ab14SFam Zheng if (ret) { 420019d6b8fSAnthony Liguori goto fail; 421019d6b8fSAnthony Liguori } 422019d6b8fSAnthony Liguori return 0; 423019d6b8fSAnthony Liguori fail: 424b3976d3cSFam Zheng vmdk_free_extents(bs); 425b4b3ab14SFam Zheng return ret; 426b4b3ab14SFam Zheng } 427b4b3ab14SFam Zheng 4287fa60fa3SFam Zheng /* find an option value out of descriptor file */ 4297fa60fa3SFam Zheng static int vmdk_parse_description(const char *desc, const char *opt_name, 4307fa60fa3SFam Zheng char *buf, int buf_size) 4317fa60fa3SFam Zheng { 4327fa60fa3SFam Zheng char *opt_pos, *opt_end; 4337fa60fa3SFam Zheng const char *end = desc + strlen(desc); 4347fa60fa3SFam Zheng 4357fa60fa3SFam Zheng opt_pos = strstr(desc, opt_name); 4367fa60fa3SFam Zheng if (!opt_pos) { 4377fa60fa3SFam Zheng return -1; 4387fa60fa3SFam Zheng } 4397fa60fa3SFam Zheng /* Skip "=\"" following opt_name */ 4407fa60fa3SFam Zheng opt_pos += strlen(opt_name) + 2; 4417fa60fa3SFam Zheng if (opt_pos >= end) { 4427fa60fa3SFam Zheng return -1; 4437fa60fa3SFam Zheng } 4447fa60fa3SFam Zheng opt_end = opt_pos; 4457fa60fa3SFam Zheng while (opt_end < end && *opt_end != '"') { 4467fa60fa3SFam Zheng opt_end++; 4477fa60fa3SFam Zheng } 4487fa60fa3SFam Zheng if (opt_end == end || buf_size < opt_end - opt_pos + 1) { 4497fa60fa3SFam Zheng return -1; 4507fa60fa3SFam Zheng } 4517fa60fa3SFam Zheng pstrcpy(buf, opt_end - opt_pos + 1, opt_pos); 4527fa60fa3SFam Zheng return 0; 4537fa60fa3SFam Zheng } 4547fa60fa3SFam Zheng 4557fa60fa3SFam Zheng static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, 4567fa60fa3SFam Zheng const char *desc_file_path) 4577fa60fa3SFam Zheng { 4587fa60fa3SFam Zheng int ret; 4597fa60fa3SFam Zheng char access[11]; 4607fa60fa3SFam Zheng char type[11]; 4617fa60fa3SFam Zheng char fname[512]; 4627fa60fa3SFam Zheng const char *p = desc; 4637fa60fa3SFam Zheng int64_t sectors = 0; 4647fa60fa3SFam Zheng int64_t flat_offset; 4657fa60fa3SFam Zheng 4667fa60fa3SFam Zheng while (*p) { 4677fa60fa3SFam Zheng /* parse extent line: 4687fa60fa3SFam Zheng * RW [size in sectors] FLAT "file-name.vmdk" OFFSET 4697fa60fa3SFam Zheng * or 4707fa60fa3SFam Zheng * RW [size in sectors] SPARSE "file-name.vmdk" 4717fa60fa3SFam Zheng */ 4727fa60fa3SFam Zheng flat_offset = -1; 4737fa60fa3SFam Zheng ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64, 4747fa60fa3SFam Zheng access, §ors, type, fname, &flat_offset); 4757fa60fa3SFam Zheng if (ret < 4 || strcmp(access, "RW")) { 4767fa60fa3SFam Zheng goto next_line; 4777fa60fa3SFam Zheng } else if (!strcmp(type, "FLAT")) { 4787fa60fa3SFam Zheng if (ret != 5 || flat_offset < 0) { 4797fa60fa3SFam Zheng return -EINVAL; 4807fa60fa3SFam Zheng } 4817fa60fa3SFam Zheng } else if (ret != 4) { 4827fa60fa3SFam Zheng return -EINVAL; 4837fa60fa3SFam Zheng } 4847fa60fa3SFam Zheng 4857fa60fa3SFam Zheng /* trim the quotation marks around */ 4867fa60fa3SFam Zheng if (fname[0] == '"') { 4877fa60fa3SFam Zheng memmove(fname, fname + 1, strlen(fname)); 4887fa60fa3SFam Zheng if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') { 4897fa60fa3SFam Zheng return -EINVAL; 4907fa60fa3SFam Zheng } 4917fa60fa3SFam Zheng fname[strlen(fname) - 1] = '\0'; 4927fa60fa3SFam Zheng } 4937fa60fa3SFam Zheng if (sectors <= 0 || 4947fa60fa3SFam Zheng (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) || 4957fa60fa3SFam Zheng (strcmp(access, "RW"))) { 4967fa60fa3SFam Zheng goto next_line; 4977fa60fa3SFam Zheng } 4987fa60fa3SFam Zheng 4997fa60fa3SFam Zheng /* save to extents array */ 5007fa60fa3SFam Zheng if (!strcmp(type, "FLAT")) { 5017fa60fa3SFam Zheng /* FLAT extent */ 5027fa60fa3SFam Zheng char extent_path[PATH_MAX]; 5037fa60fa3SFam Zheng BlockDriverState *extent_file; 5047fa60fa3SFam Zheng VmdkExtent *extent; 5057fa60fa3SFam Zheng 5067fa60fa3SFam Zheng path_combine(extent_path, sizeof(extent_path), 5077fa60fa3SFam Zheng desc_file_path, fname); 5087fa60fa3SFam Zheng ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags); 5097fa60fa3SFam Zheng if (ret) { 5107fa60fa3SFam Zheng return ret; 5117fa60fa3SFam Zheng } 5127fa60fa3SFam Zheng extent = vmdk_add_extent(bs, extent_file, true, sectors, 5137fa60fa3SFam Zheng 0, 0, 0, 0, sectors); 5147fa60fa3SFam Zheng extent->flat_start_offset = flat_offset; 5157fa60fa3SFam Zheng } else { 5167fa60fa3SFam Zheng /* SPARSE extent, not supported for now */ 5177fa60fa3SFam Zheng fprintf(stderr, 5187fa60fa3SFam Zheng "VMDK: Not supported extent type \"%s\""".\n", type); 5197fa60fa3SFam Zheng return -ENOTSUP; 5207fa60fa3SFam Zheng } 5217fa60fa3SFam Zheng next_line: 5227fa60fa3SFam Zheng /* move to next line */ 5237fa60fa3SFam Zheng while (*p && *p != '\n') { 5247fa60fa3SFam Zheng p++; 5257fa60fa3SFam Zheng } 5267fa60fa3SFam Zheng p++; 5277fa60fa3SFam Zheng } 5287fa60fa3SFam Zheng return 0; 5297fa60fa3SFam Zheng } 5307fa60fa3SFam Zheng 5317fa60fa3SFam Zheng static int vmdk_open_desc_file(BlockDriverState *bs, int flags) 5327fa60fa3SFam Zheng { 5337fa60fa3SFam Zheng int ret; 5347fa60fa3SFam Zheng char buf[2048]; 5357fa60fa3SFam Zheng char ct[128]; 5367fa60fa3SFam Zheng BDRVVmdkState *s = bs->opaque; 5377fa60fa3SFam Zheng 5387fa60fa3SFam Zheng ret = bdrv_pread(bs->file, 0, buf, sizeof(buf)); 5397fa60fa3SFam Zheng if (ret < 0) { 5407fa60fa3SFam Zheng return ret; 5417fa60fa3SFam Zheng } 5427fa60fa3SFam Zheng buf[2047] = '\0'; 5437fa60fa3SFam Zheng if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { 5447fa60fa3SFam Zheng return -EINVAL; 5457fa60fa3SFam Zheng } 5467fa60fa3SFam Zheng if (strcmp(ct, "monolithicFlat")) { 5477fa60fa3SFam Zheng fprintf(stderr, 5487fa60fa3SFam Zheng "VMDK: Not supported image type \"%s\""".\n", ct); 5497fa60fa3SFam Zheng return -ENOTSUP; 5507fa60fa3SFam Zheng } 5517fa60fa3SFam Zheng s->desc_offset = 0; 5527fa60fa3SFam Zheng ret = vmdk_parse_extents(buf, bs, bs->file->filename); 5537fa60fa3SFam Zheng if (ret) { 5547fa60fa3SFam Zheng return ret; 5557fa60fa3SFam Zheng } 5567fa60fa3SFam Zheng 5577fa60fa3SFam Zheng /* try to open parent images, if exist */ 5587fa60fa3SFam Zheng if (vmdk_parent_open(bs)) { 5597fa60fa3SFam Zheng qemu_free(s->extents); 5607fa60fa3SFam Zheng return -EINVAL; 5617fa60fa3SFam Zheng } 5627fa60fa3SFam Zheng s->parent_cid = vmdk_read_cid(bs, 1); 5637fa60fa3SFam Zheng return 0; 5647fa60fa3SFam Zheng } 5657fa60fa3SFam Zheng 566b4b3ab14SFam Zheng static int vmdk_open(BlockDriverState *bs, int flags) 567b4b3ab14SFam Zheng { 568b4b3ab14SFam Zheng uint32_t magic; 569b4b3ab14SFam Zheng 570b4b3ab14SFam Zheng if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) { 571b4b3ab14SFam Zheng return -EIO; 572b4b3ab14SFam Zheng } 573b4b3ab14SFam Zheng 574b4b3ab14SFam Zheng magic = be32_to_cpu(magic); 575b4b3ab14SFam Zheng if (magic == VMDK3_MAGIC) { 576b4b3ab14SFam Zheng return vmdk_open_vmdk3(bs, flags); 577b4b3ab14SFam Zheng } else if (magic == VMDK4_MAGIC) { 578b4b3ab14SFam Zheng return vmdk_open_vmdk4(bs, flags); 579b4b3ab14SFam Zheng } else { 5807fa60fa3SFam Zheng return vmdk_open_desc_file(bs, flags); 581b4b3ab14SFam Zheng } 582019d6b8fSAnthony Liguori } 583019d6b8fSAnthony Liguori 584b3976d3cSFam Zheng static int get_whole_cluster(BlockDriverState *bs, 585b3976d3cSFam Zheng VmdkExtent *extent, 586b3976d3cSFam Zheng uint64_t cluster_offset, 587b3976d3cSFam Zheng uint64_t offset, 588b3976d3cSFam Zheng bool allocate) 589019d6b8fSAnthony Liguori { 590b3976d3cSFam Zheng /* 128 sectors * 512 bytes each = grain size 64KB */ 591b3976d3cSFam Zheng uint8_t whole_grain[extent->cluster_sectors * 512]; 592019d6b8fSAnthony Liguori 5930e69c543SFam Zheng /* we will be here if it's first write on non-exist grain(cluster). 5940e69c543SFam Zheng * try to read from parent image, if exist */ 595b171271aSKevin Wolf if (bs->backing_hd) { 596c336500dSKevin Wolf int ret; 597019d6b8fSAnthony Liguori 598019d6b8fSAnthony Liguori if (!vmdk_is_cid_valid(bs)) 599019d6b8fSAnthony Liguori return -1; 600019d6b8fSAnthony Liguori 6010e69c543SFam Zheng /* floor offset to cluster */ 6020e69c543SFam Zheng offset -= offset % (extent->cluster_sectors * 512); 603c336500dSKevin Wolf ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain, 604b3976d3cSFam Zheng extent->cluster_sectors); 605c336500dSKevin Wolf if (ret < 0) { 606019d6b8fSAnthony Liguori return -1; 607c336500dSKevin Wolf } 608019d6b8fSAnthony Liguori 6090e69c543SFam Zheng /* Write grain only into the active image */ 610b3976d3cSFam Zheng ret = bdrv_write(extent->file, cluster_offset, whole_grain, 611b3976d3cSFam Zheng extent->cluster_sectors); 612c336500dSKevin Wolf if (ret < 0) { 613019d6b8fSAnthony Liguori return -1; 614019d6b8fSAnthony Liguori } 615019d6b8fSAnthony Liguori } 616019d6b8fSAnthony Liguori return 0; 617019d6b8fSAnthony Liguori } 618019d6b8fSAnthony Liguori 619b3976d3cSFam Zheng static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data) 620019d6b8fSAnthony Liguori { 621019d6b8fSAnthony Liguori /* update L2 table */ 622b3976d3cSFam Zheng if (bdrv_pwrite_sync( 623b3976d3cSFam Zheng extent->file, 624b3976d3cSFam Zheng ((int64_t)m_data->l2_offset * 512) 625b3976d3cSFam Zheng + (m_data->l2_index * sizeof(m_data->offset)), 626b3976d3cSFam Zheng &(m_data->offset), 627b3976d3cSFam Zheng sizeof(m_data->offset) 628b3976d3cSFam Zheng ) < 0) { 629019d6b8fSAnthony Liguori return -1; 630b3976d3cSFam Zheng } 631019d6b8fSAnthony Liguori /* update backup L2 table */ 632b3976d3cSFam Zheng if (extent->l1_backup_table_offset != 0) { 633b3976d3cSFam Zheng m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; 634b3976d3cSFam Zheng if (bdrv_pwrite_sync( 635b3976d3cSFam Zheng extent->file, 636b3976d3cSFam Zheng ((int64_t)m_data->l2_offset * 512) 637b3976d3cSFam Zheng + (m_data->l2_index * sizeof(m_data->offset)), 638b3976d3cSFam Zheng &(m_data->offset), sizeof(m_data->offset) 639b3976d3cSFam Zheng ) < 0) { 640019d6b8fSAnthony Liguori return -1; 641019d6b8fSAnthony Liguori } 642b3976d3cSFam Zheng } 643019d6b8fSAnthony Liguori 644019d6b8fSAnthony Liguori return 0; 645019d6b8fSAnthony Liguori } 646019d6b8fSAnthony Liguori 64791b85bd3SFam Zheng static int get_cluster_offset(BlockDriverState *bs, 648b3976d3cSFam Zheng VmdkExtent *extent, 649b3976d3cSFam Zheng VmdkMetaData *m_data, 65091b85bd3SFam Zheng uint64_t offset, 65191b85bd3SFam Zheng int allocate, 65291b85bd3SFam Zheng uint64_t *cluster_offset) 653019d6b8fSAnthony Liguori { 654019d6b8fSAnthony Liguori unsigned int l1_index, l2_offset, l2_index; 655019d6b8fSAnthony Liguori int min_index, i, j; 656019d6b8fSAnthony Liguori uint32_t min_count, *l2_table, tmp = 0; 657019d6b8fSAnthony Liguori 658019d6b8fSAnthony Liguori if (m_data) 659019d6b8fSAnthony Liguori m_data->valid = 0; 66091b85bd3SFam Zheng if (extent->flat) { 6617fa60fa3SFam Zheng *cluster_offset = extent->flat_start_offset; 66291b85bd3SFam Zheng return 0; 66391b85bd3SFam Zheng } 664019d6b8fSAnthony Liguori 665b3976d3cSFam Zheng l1_index = (offset >> 9) / extent->l1_entry_sectors; 666b3976d3cSFam Zheng if (l1_index >= extent->l1_size) { 66791b85bd3SFam Zheng return -1; 668b3976d3cSFam Zheng } 669b3976d3cSFam Zheng l2_offset = extent->l1_table[l1_index]; 670b3976d3cSFam Zheng if (!l2_offset) { 67191b85bd3SFam Zheng return -1; 672b3976d3cSFam Zheng } 673019d6b8fSAnthony Liguori for (i = 0; i < L2_CACHE_SIZE; i++) { 674b3976d3cSFam Zheng if (l2_offset == extent->l2_cache_offsets[i]) { 675019d6b8fSAnthony Liguori /* increment the hit count */ 676b3976d3cSFam Zheng if (++extent->l2_cache_counts[i] == 0xffffffff) { 677019d6b8fSAnthony Liguori for (j = 0; j < L2_CACHE_SIZE; j++) { 678b3976d3cSFam Zheng extent->l2_cache_counts[j] >>= 1; 679019d6b8fSAnthony Liguori } 680019d6b8fSAnthony Liguori } 681b3976d3cSFam Zheng l2_table = extent->l2_cache + (i * extent->l2_size); 682019d6b8fSAnthony Liguori goto found; 683019d6b8fSAnthony Liguori } 684019d6b8fSAnthony Liguori } 685019d6b8fSAnthony Liguori /* not found: load a new entry in the least used one */ 686019d6b8fSAnthony Liguori min_index = 0; 687019d6b8fSAnthony Liguori min_count = 0xffffffff; 688019d6b8fSAnthony Liguori for (i = 0; i < L2_CACHE_SIZE; i++) { 689b3976d3cSFam Zheng if (extent->l2_cache_counts[i] < min_count) { 690b3976d3cSFam Zheng min_count = extent->l2_cache_counts[i]; 691019d6b8fSAnthony Liguori min_index = i; 692019d6b8fSAnthony Liguori } 693019d6b8fSAnthony Liguori } 694b3976d3cSFam Zheng l2_table = extent->l2_cache + (min_index * extent->l2_size); 695b3976d3cSFam Zheng if (bdrv_pread( 696b3976d3cSFam Zheng extent->file, 697b3976d3cSFam Zheng (int64_t)l2_offset * 512, 698b3976d3cSFam Zheng l2_table, 699b3976d3cSFam Zheng extent->l2_size * sizeof(uint32_t) 700b3976d3cSFam Zheng ) != extent->l2_size * sizeof(uint32_t)) { 70191b85bd3SFam Zheng return -1; 702b3976d3cSFam Zheng } 703019d6b8fSAnthony Liguori 704b3976d3cSFam Zheng extent->l2_cache_offsets[min_index] = l2_offset; 705b3976d3cSFam Zheng extent->l2_cache_counts[min_index] = 1; 706019d6b8fSAnthony Liguori found: 707b3976d3cSFam Zheng l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; 70891b85bd3SFam Zheng *cluster_offset = le32_to_cpu(l2_table[l2_index]); 709019d6b8fSAnthony Liguori 71091b85bd3SFam Zheng if (!*cluster_offset) { 71191b85bd3SFam Zheng if (!allocate) { 71291b85bd3SFam Zheng return -1; 71391b85bd3SFam Zheng } 7149949f97eSKevin Wolf 715019d6b8fSAnthony Liguori // Avoid the L2 tables update for the images that have snapshots. 71691b85bd3SFam Zheng *cluster_offset = bdrv_getlength(extent->file); 717b3976d3cSFam Zheng bdrv_truncate( 718b3976d3cSFam Zheng extent->file, 71991b85bd3SFam Zheng *cluster_offset + (extent->cluster_sectors << 9) 720b3976d3cSFam Zheng ); 721019d6b8fSAnthony Liguori 72291b85bd3SFam Zheng *cluster_offset >>= 9; 72391b85bd3SFam Zheng tmp = cpu_to_le32(*cluster_offset); 724019d6b8fSAnthony Liguori l2_table[l2_index] = tmp; 7259949f97eSKevin Wolf 726019d6b8fSAnthony Liguori /* First of all we write grain itself, to avoid race condition 727019d6b8fSAnthony Liguori * that may to corrupt the image. 728019d6b8fSAnthony Liguori * This problem may occur because of insufficient space on host disk 729019d6b8fSAnthony Liguori * or inappropriate VM shutdown. 730019d6b8fSAnthony Liguori */ 731b3976d3cSFam Zheng if (get_whole_cluster( 73291b85bd3SFam Zheng bs, extent, *cluster_offset, offset, allocate) == -1) 73391b85bd3SFam Zheng return -1; 734019d6b8fSAnthony Liguori 735019d6b8fSAnthony Liguori if (m_data) { 736019d6b8fSAnthony Liguori m_data->offset = tmp; 737019d6b8fSAnthony Liguori m_data->l1_index = l1_index; 738019d6b8fSAnthony Liguori m_data->l2_index = l2_index; 739019d6b8fSAnthony Liguori m_data->l2_offset = l2_offset; 740019d6b8fSAnthony Liguori m_data->valid = 1; 741019d6b8fSAnthony Liguori } 742019d6b8fSAnthony Liguori } 74391b85bd3SFam Zheng *cluster_offset <<= 9; 74491b85bd3SFam Zheng return 0; 745019d6b8fSAnthony Liguori } 746019d6b8fSAnthony Liguori 747b3976d3cSFam Zheng static VmdkExtent *find_extent(BDRVVmdkState *s, 748b3976d3cSFam Zheng int64_t sector_num, VmdkExtent *start_hint) 749b3976d3cSFam Zheng { 750b3976d3cSFam Zheng VmdkExtent *extent = start_hint; 751b3976d3cSFam Zheng 752b3976d3cSFam Zheng if (!extent) { 753b3976d3cSFam Zheng extent = &s->extents[0]; 754b3976d3cSFam Zheng } 755b3976d3cSFam Zheng while (extent < &s->extents[s->num_extents]) { 756b3976d3cSFam Zheng if (sector_num < extent->end_sector) { 757b3976d3cSFam Zheng return extent; 758b3976d3cSFam Zheng } 759b3976d3cSFam Zheng extent++; 760b3976d3cSFam Zheng } 761b3976d3cSFam Zheng return NULL; 762b3976d3cSFam Zheng } 763b3976d3cSFam Zheng 764019d6b8fSAnthony Liguori static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, 765019d6b8fSAnthony Liguori int nb_sectors, int *pnum) 766019d6b8fSAnthony Liguori { 767019d6b8fSAnthony Liguori BDRVVmdkState *s = bs->opaque; 768b3976d3cSFam Zheng int64_t index_in_cluster, n, ret; 769b3976d3cSFam Zheng uint64_t offset; 770b3976d3cSFam Zheng VmdkExtent *extent; 771b3976d3cSFam Zheng 772b3976d3cSFam Zheng extent = find_extent(s, sector_num, NULL); 773b3976d3cSFam Zheng if (!extent) { 774b3976d3cSFam Zheng return 0; 775b3976d3cSFam Zheng } 77691b85bd3SFam Zheng ret = get_cluster_offset(bs, extent, NULL, 77791b85bd3SFam Zheng sector_num * 512, 0, &offset); 77891b85bd3SFam Zheng /* get_cluster_offset returning 0 means success */ 77991b85bd3SFam Zheng ret = !ret; 78091b85bd3SFam Zheng 781b3976d3cSFam Zheng index_in_cluster = sector_num % extent->cluster_sectors; 782b3976d3cSFam Zheng n = extent->cluster_sectors - index_in_cluster; 783019d6b8fSAnthony Liguori if (n > nb_sectors) 784019d6b8fSAnthony Liguori n = nb_sectors; 785019d6b8fSAnthony Liguori *pnum = n; 786b3976d3cSFam Zheng return ret; 787019d6b8fSAnthony Liguori } 788019d6b8fSAnthony Liguori 789019d6b8fSAnthony Liguori static int vmdk_read(BlockDriverState *bs, int64_t sector_num, 790019d6b8fSAnthony Liguori uint8_t *buf, int nb_sectors) 791019d6b8fSAnthony Liguori { 792019d6b8fSAnthony Liguori BDRVVmdkState *s = bs->opaque; 793b3976d3cSFam Zheng int ret; 794b3976d3cSFam Zheng uint64_t n, index_in_cluster; 795b3976d3cSFam Zheng VmdkExtent *extent = NULL; 796019d6b8fSAnthony Liguori uint64_t cluster_offset; 797019d6b8fSAnthony Liguori 798019d6b8fSAnthony Liguori while (nb_sectors > 0) { 799b3976d3cSFam Zheng extent = find_extent(s, sector_num, extent); 800b3976d3cSFam Zheng if (!extent) { 801b3976d3cSFam Zheng return -EIO; 802b3976d3cSFam Zheng } 80391b85bd3SFam Zheng ret = get_cluster_offset( 80491b85bd3SFam Zheng bs, extent, NULL, 80591b85bd3SFam Zheng sector_num << 9, 0, &cluster_offset); 806b3976d3cSFam Zheng index_in_cluster = sector_num % extent->cluster_sectors; 807b3976d3cSFam Zheng n = extent->cluster_sectors - index_in_cluster; 808019d6b8fSAnthony Liguori if (n > nb_sectors) 809019d6b8fSAnthony Liguori n = nb_sectors; 81091b85bd3SFam Zheng if (ret) { 81191b85bd3SFam Zheng /* if not allocated, try to read from parent image, if exist */ 812b171271aSKevin Wolf if (bs->backing_hd) { 813019d6b8fSAnthony Liguori if (!vmdk_is_cid_valid(bs)) 8147fa60fa3SFam Zheng return -EINVAL; 815b171271aSKevin Wolf ret = bdrv_read(bs->backing_hd, sector_num, buf, n); 816019d6b8fSAnthony Liguori if (ret < 0) 8177fa60fa3SFam Zheng return ret; 818019d6b8fSAnthony Liguori } else { 819019d6b8fSAnthony Liguori memset(buf, 0, 512 * n); 820019d6b8fSAnthony Liguori } 821019d6b8fSAnthony Liguori } else { 8227fa60fa3SFam Zheng ret = bdrv_pread(extent->file, 8237fa60fa3SFam Zheng cluster_offset + index_in_cluster * 512, 8247fa60fa3SFam Zheng buf, n * 512); 8257fa60fa3SFam Zheng if (ret < 0) { 8267fa60fa3SFam Zheng return ret; 8277fa60fa3SFam Zheng } 828019d6b8fSAnthony Liguori } 829019d6b8fSAnthony Liguori nb_sectors -= n; 830019d6b8fSAnthony Liguori sector_num += n; 831019d6b8fSAnthony Liguori buf += n * 512; 832019d6b8fSAnthony Liguori } 833019d6b8fSAnthony Liguori return 0; 834019d6b8fSAnthony Liguori } 835019d6b8fSAnthony Liguori 836019d6b8fSAnthony Liguori static int vmdk_write(BlockDriverState *bs, int64_t sector_num, 837019d6b8fSAnthony Liguori const uint8_t *buf, int nb_sectors) 838019d6b8fSAnthony Liguori { 839019d6b8fSAnthony Liguori BDRVVmdkState *s = bs->opaque; 840b3976d3cSFam Zheng VmdkExtent *extent = NULL; 84191b85bd3SFam Zheng int n, ret; 842b3976d3cSFam Zheng int64_t index_in_cluster; 843019d6b8fSAnthony Liguori uint64_t cluster_offset; 844b3976d3cSFam Zheng VmdkMetaData m_data; 845019d6b8fSAnthony Liguori 846019d6b8fSAnthony Liguori if (sector_num > bs->total_sectors) { 847019d6b8fSAnthony Liguori fprintf(stderr, 848019d6b8fSAnthony Liguori "(VMDK) Wrong offset: sector_num=0x%" PRIx64 849019d6b8fSAnthony Liguori " total_sectors=0x%" PRIx64 "\n", 850019d6b8fSAnthony Liguori sector_num, bs->total_sectors); 8517fa60fa3SFam Zheng return -EIO; 852019d6b8fSAnthony Liguori } 853019d6b8fSAnthony Liguori 854019d6b8fSAnthony Liguori while (nb_sectors > 0) { 855b3976d3cSFam Zheng extent = find_extent(s, sector_num, extent); 856b3976d3cSFam Zheng if (!extent) { 857b3976d3cSFam Zheng return -EIO; 858b3976d3cSFam Zheng } 85991b85bd3SFam Zheng ret = get_cluster_offset( 860b3976d3cSFam Zheng bs, 861b3976d3cSFam Zheng extent, 862b3976d3cSFam Zheng &m_data, 86391b85bd3SFam Zheng sector_num << 9, 1, 86491b85bd3SFam Zheng &cluster_offset); 86591b85bd3SFam Zheng if (ret) { 86691b85bd3SFam Zheng return -EINVAL; 867b3976d3cSFam Zheng } 868b3976d3cSFam Zheng index_in_cluster = sector_num % extent->cluster_sectors; 869b3976d3cSFam Zheng n = extent->cluster_sectors - index_in_cluster; 870b3976d3cSFam Zheng if (n > nb_sectors) { 871019d6b8fSAnthony Liguori n = nb_sectors; 872b3976d3cSFam Zheng } 873019d6b8fSAnthony Liguori 8747fa60fa3SFam Zheng ret = bdrv_pwrite(extent->file, 875b3976d3cSFam Zheng cluster_offset + index_in_cluster * 512, 8767fa60fa3SFam Zheng buf, 8777fa60fa3SFam Zheng n * 512); 8787fa60fa3SFam Zheng if (ret < 0) { 8797fa60fa3SFam Zheng return ret; 880b3976d3cSFam Zheng } 881019d6b8fSAnthony Liguori if (m_data.valid) { 882019d6b8fSAnthony Liguori /* update L2 tables */ 883b3976d3cSFam Zheng if (vmdk_L2update(extent, &m_data) == -1) { 8847fa60fa3SFam Zheng return -EIO; 885019d6b8fSAnthony Liguori } 886b3976d3cSFam Zheng } 887019d6b8fSAnthony Liguori nb_sectors -= n; 888019d6b8fSAnthony Liguori sector_num += n; 889019d6b8fSAnthony Liguori buf += n * 512; 890019d6b8fSAnthony Liguori 891019d6b8fSAnthony Liguori // update CID on the first write every time the virtual disk is opened 89269b4d86dSFam Zheng if (!s->cid_updated) { 893019d6b8fSAnthony Liguori vmdk_write_cid(bs, time(NULL)); 89469b4d86dSFam Zheng s->cid_updated = true; 895019d6b8fSAnthony Liguori } 896019d6b8fSAnthony Liguori } 897019d6b8fSAnthony Liguori return 0; 898019d6b8fSAnthony Liguori } 899019d6b8fSAnthony Liguori 900*f66fd6c3SFam Zheng 901*f66fd6c3SFam Zheng static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat) 902019d6b8fSAnthony Liguori { 903*f66fd6c3SFam Zheng int ret, i; 904*f66fd6c3SFam Zheng int fd = 0; 905019d6b8fSAnthony Liguori VMDK4Header header; 906019d6b8fSAnthony Liguori uint32_t tmp, magic, grains, gd_size, gt_size, gt_count; 9070e7e1989SKevin Wolf 908*f66fd6c3SFam Zheng fd = open( 909*f66fd6c3SFam Zheng filename, 910*f66fd6c3SFam Zheng O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 911019d6b8fSAnthony Liguori 0644); 912*f66fd6c3SFam Zheng if (fd < 0) { 913b781cce5SJuan Quintela return -errno; 914*f66fd6c3SFam Zheng } 915*f66fd6c3SFam Zheng if (flat) { 916*f66fd6c3SFam Zheng ret = ftruncate(fd, filesize); 917*f66fd6c3SFam Zheng if (ret < 0) { 918*f66fd6c3SFam Zheng ret = -errno; 919*f66fd6c3SFam Zheng } 920*f66fd6c3SFam Zheng goto exit; 921*f66fd6c3SFam Zheng } 922019d6b8fSAnthony Liguori magic = cpu_to_be32(VMDK4_MAGIC); 923019d6b8fSAnthony Liguori memset(&header, 0, sizeof(header)); 92416372ff0SAlexander Graf header.version = 1; 92516372ff0SAlexander Graf header.flags = 3; /* ?? */ 926*f66fd6c3SFam Zheng header.capacity = filesize / 512; 92716372ff0SAlexander Graf header.granularity = 128; 92816372ff0SAlexander Graf header.num_gtes_per_gte = 512; 929019d6b8fSAnthony Liguori 930*f66fd6c3SFam Zheng grains = (filesize / 512 + header.granularity - 1) / header.granularity; 931019d6b8fSAnthony Liguori gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; 932*f66fd6c3SFam Zheng gt_count = 933*f66fd6c3SFam Zheng (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; 934019d6b8fSAnthony Liguori gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; 935019d6b8fSAnthony Liguori 936019d6b8fSAnthony Liguori header.desc_offset = 1; 937019d6b8fSAnthony Liguori header.desc_size = 20; 938019d6b8fSAnthony Liguori header.rgd_offset = header.desc_offset + header.desc_size; 939019d6b8fSAnthony Liguori header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count); 940019d6b8fSAnthony Liguori header.grain_offset = 941019d6b8fSAnthony Liguori ((header.gd_offset + gd_size + (gt_size * gt_count) + 942019d6b8fSAnthony Liguori header.granularity - 1) / header.granularity) * 943019d6b8fSAnthony Liguori header.granularity; 94416372ff0SAlexander Graf /* swap endianness for all header fields */ 94516372ff0SAlexander Graf header.version = cpu_to_le32(header.version); 94616372ff0SAlexander Graf header.flags = cpu_to_le32(header.flags); 94716372ff0SAlexander Graf header.capacity = cpu_to_le64(header.capacity); 94816372ff0SAlexander Graf header.granularity = cpu_to_le64(header.granularity); 94916372ff0SAlexander Graf header.num_gtes_per_gte = cpu_to_le32(header.num_gtes_per_gte); 950019d6b8fSAnthony Liguori header.desc_offset = cpu_to_le64(header.desc_offset); 951019d6b8fSAnthony Liguori header.desc_size = cpu_to_le64(header.desc_size); 952019d6b8fSAnthony Liguori header.rgd_offset = cpu_to_le64(header.rgd_offset); 953019d6b8fSAnthony Liguori header.gd_offset = cpu_to_le64(header.gd_offset); 954019d6b8fSAnthony Liguori header.grain_offset = cpu_to_le64(header.grain_offset); 955019d6b8fSAnthony Liguori 956019d6b8fSAnthony Liguori header.check_bytes[0] = 0xa; 957019d6b8fSAnthony Liguori header.check_bytes[1] = 0x20; 958019d6b8fSAnthony Liguori header.check_bytes[2] = 0xd; 959019d6b8fSAnthony Liguori header.check_bytes[3] = 0xa; 960019d6b8fSAnthony Liguori 961019d6b8fSAnthony Liguori /* write all the data */ 9621640366cSKirill A. Shutemov ret = qemu_write_full(fd, &magic, sizeof(magic)); 9631640366cSKirill A. Shutemov if (ret != sizeof(magic)) { 964b781cce5SJuan Quintela ret = -errno; 9651640366cSKirill A. Shutemov goto exit; 9661640366cSKirill A. Shutemov } 9671640366cSKirill A. Shutemov ret = qemu_write_full(fd, &header, sizeof(header)); 9681640366cSKirill A. Shutemov if (ret != sizeof(header)) { 969b781cce5SJuan Quintela ret = -errno; 9701640366cSKirill A. Shutemov goto exit; 9711640366cSKirill A. Shutemov } 972019d6b8fSAnthony Liguori 97316372ff0SAlexander Graf ret = ftruncate(fd, le64_to_cpu(header.grain_offset) << 9); 9741640366cSKirill A. Shutemov if (ret < 0) { 975b781cce5SJuan Quintela ret = -errno; 9761640366cSKirill A. Shutemov goto exit; 9771640366cSKirill A. Shutemov } 978019d6b8fSAnthony Liguori 979019d6b8fSAnthony Liguori /* write grain directory */ 980019d6b8fSAnthony Liguori lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); 98116372ff0SAlexander Graf for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_size; 9821640366cSKirill A. Shutemov i < gt_count; i++, tmp += gt_size) { 9831640366cSKirill A. Shutemov ret = qemu_write_full(fd, &tmp, sizeof(tmp)); 9841640366cSKirill A. Shutemov if (ret != sizeof(tmp)) { 985b781cce5SJuan Quintela ret = -errno; 9861640366cSKirill A. Shutemov goto exit; 9871640366cSKirill A. Shutemov } 9881640366cSKirill A. Shutemov } 989019d6b8fSAnthony Liguori 990019d6b8fSAnthony Liguori /* write backup grain directory */ 991019d6b8fSAnthony Liguori lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); 99216372ff0SAlexander Graf for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_size; 9931640366cSKirill A. Shutemov i < gt_count; i++, tmp += gt_size) { 9941640366cSKirill A. Shutemov ret = qemu_write_full(fd, &tmp, sizeof(tmp)); 9951640366cSKirill A. Shutemov if (ret != sizeof(tmp)) { 996b781cce5SJuan Quintela ret = -errno; 9971640366cSKirill A. Shutemov goto exit; 9981640366cSKirill A. Shutemov } 9991640366cSKirill A. Shutemov } 1000019d6b8fSAnthony Liguori 1001*f66fd6c3SFam Zheng ret = 0; 1002*f66fd6c3SFam Zheng exit: 1003*f66fd6c3SFam Zheng close(fd); 1004*f66fd6c3SFam Zheng return ret; 1005*f66fd6c3SFam Zheng } 1006019d6b8fSAnthony Liguori 1007*f66fd6c3SFam Zheng static int filename_decompose(const char *filename, char *path, char *prefix, 1008*f66fd6c3SFam Zheng char *postfix, size_t buf_len) 1009*f66fd6c3SFam Zheng { 1010*f66fd6c3SFam Zheng const char *p, *q; 1011*f66fd6c3SFam Zheng 1012*f66fd6c3SFam Zheng if (filename == NULL || !strlen(filename)) { 1013*f66fd6c3SFam Zheng fprintf(stderr, "Vmdk: no filename provided.\n"); 1014*f66fd6c3SFam Zheng return -1; 1015*f66fd6c3SFam Zheng } 1016*f66fd6c3SFam Zheng p = strrchr(filename, '/'); 1017*f66fd6c3SFam Zheng if (p == NULL) { 1018*f66fd6c3SFam Zheng p = strrchr(filename, '\\'); 1019*f66fd6c3SFam Zheng } 1020*f66fd6c3SFam Zheng if (p == NULL) { 1021*f66fd6c3SFam Zheng p = strrchr(filename, ':'); 1022*f66fd6c3SFam Zheng } 1023*f66fd6c3SFam Zheng if (p != NULL) { 1024*f66fd6c3SFam Zheng p++; 1025*f66fd6c3SFam Zheng if (p - filename >= buf_len) { 1026*f66fd6c3SFam Zheng return -1; 1027*f66fd6c3SFam Zheng } 1028*f66fd6c3SFam Zheng pstrcpy(path, p - filename + 1, filename); 1029*f66fd6c3SFam Zheng } else { 1030*f66fd6c3SFam Zheng p = filename; 1031*f66fd6c3SFam Zheng path[0] = '\0'; 1032*f66fd6c3SFam Zheng } 1033*f66fd6c3SFam Zheng q = strrchr(p, '.'); 1034*f66fd6c3SFam Zheng if (q == NULL) { 1035*f66fd6c3SFam Zheng pstrcpy(prefix, buf_len, p); 1036*f66fd6c3SFam Zheng postfix[0] = '\0'; 1037*f66fd6c3SFam Zheng } else { 1038*f66fd6c3SFam Zheng if (q - p >= buf_len) { 1039*f66fd6c3SFam Zheng return -1; 1040*f66fd6c3SFam Zheng } 1041*f66fd6c3SFam Zheng pstrcpy(prefix, q - p + 1, p); 1042*f66fd6c3SFam Zheng pstrcpy(postfix, buf_len, q); 1043*f66fd6c3SFam Zheng } 1044*f66fd6c3SFam Zheng return 0; 1045*f66fd6c3SFam Zheng } 1046*f66fd6c3SFam Zheng 1047*f66fd6c3SFam Zheng static int relative_path(char *dest, int dest_size, 1048*f66fd6c3SFam Zheng const char *base, const char *target) 1049*f66fd6c3SFam Zheng { 1050*f66fd6c3SFam Zheng int i = 0; 1051*f66fd6c3SFam Zheng int n = 0; 1052*f66fd6c3SFam Zheng const char *p, *q; 1053*f66fd6c3SFam Zheng #ifdef _WIN32 1054*f66fd6c3SFam Zheng const char *sep = "\\"; 1055*f66fd6c3SFam Zheng #else 1056*f66fd6c3SFam Zheng const char *sep = "/"; 1057*f66fd6c3SFam Zheng #endif 1058*f66fd6c3SFam Zheng 1059*f66fd6c3SFam Zheng if (!(dest && base && target)) { 1060*f66fd6c3SFam Zheng return -1; 1061*f66fd6c3SFam Zheng } 1062*f66fd6c3SFam Zheng if (path_is_absolute(target)) { 1063*f66fd6c3SFam Zheng dest[dest_size - 1] = '\0'; 1064*f66fd6c3SFam Zheng strncpy(dest, target, dest_size - 1); 1065*f66fd6c3SFam Zheng return 0; 1066*f66fd6c3SFam Zheng } 1067*f66fd6c3SFam Zheng while (base[i] == target[i]) { 1068*f66fd6c3SFam Zheng i++; 1069*f66fd6c3SFam Zheng } 1070*f66fd6c3SFam Zheng p = &base[i]; 1071*f66fd6c3SFam Zheng q = &target[i]; 1072*f66fd6c3SFam Zheng while (*p) { 1073*f66fd6c3SFam Zheng if (*p == *sep) { 1074*f66fd6c3SFam Zheng n++; 1075*f66fd6c3SFam Zheng } 1076*f66fd6c3SFam Zheng p++; 1077*f66fd6c3SFam Zheng } 1078*f66fd6c3SFam Zheng dest[0] = '\0'; 1079*f66fd6c3SFam Zheng for (; n; n--) { 1080*f66fd6c3SFam Zheng pstrcat(dest, dest_size, ".."); 1081*f66fd6c3SFam Zheng pstrcat(dest, dest_size, sep); 1082*f66fd6c3SFam Zheng } 1083*f66fd6c3SFam Zheng pstrcat(dest, dest_size, q); 1084*f66fd6c3SFam Zheng return 0; 1085*f66fd6c3SFam Zheng } 1086*f66fd6c3SFam Zheng 1087*f66fd6c3SFam Zheng static int vmdk_create(const char *filename, QEMUOptionParameter *options) 1088*f66fd6c3SFam Zheng { 1089*f66fd6c3SFam Zheng int fd, idx = 0; 1090*f66fd6c3SFam Zheng char desc[BUF_SIZE]; 1091*f66fd6c3SFam Zheng int64_t total_size = 0, filesize; 1092*f66fd6c3SFam Zheng const char *backing_file = NULL; 1093*f66fd6c3SFam Zheng const char *fmt = NULL; 1094*f66fd6c3SFam Zheng int flags = 0; 1095*f66fd6c3SFam Zheng int ret = 0; 1096*f66fd6c3SFam Zheng bool flat, split; 1097*f66fd6c3SFam Zheng char ext_desc_lines[BUF_SIZE] = ""; 1098*f66fd6c3SFam Zheng char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX]; 1099*f66fd6c3SFam Zheng const int64_t split_size = 0x80000000; /* VMDK has constant split size */ 1100*f66fd6c3SFam Zheng const char *desc_extent_line; 1101*f66fd6c3SFam Zheng char parent_desc_line[BUF_SIZE] = ""; 1102*f66fd6c3SFam Zheng uint32_t parent_cid = 0xffffffff; 1103*f66fd6c3SFam Zheng const char desc_template[] = 1104*f66fd6c3SFam Zheng "# Disk DescriptorFile\n" 1105*f66fd6c3SFam Zheng "version=1\n" 1106*f66fd6c3SFam Zheng "CID=%x\n" 1107*f66fd6c3SFam Zheng "parentCID=%x\n" 1108*f66fd6c3SFam Zheng "createType=\"%s\"\n" 1109*f66fd6c3SFam Zheng "%s" 1110*f66fd6c3SFam Zheng "\n" 1111*f66fd6c3SFam Zheng "# Extent description\n" 1112*f66fd6c3SFam Zheng "%s" 1113*f66fd6c3SFam Zheng "\n" 1114*f66fd6c3SFam Zheng "# The Disk Data Base\n" 1115*f66fd6c3SFam Zheng "#DDB\n" 1116*f66fd6c3SFam Zheng "\n" 1117*f66fd6c3SFam Zheng "ddb.virtualHWVersion = \"%d\"\n" 1118*f66fd6c3SFam Zheng "ddb.geometry.cylinders = \"%" PRId64 "\"\n" 1119*f66fd6c3SFam Zheng "ddb.geometry.heads = \"16\"\n" 1120*f66fd6c3SFam Zheng "ddb.geometry.sectors = \"63\"\n" 1121*f66fd6c3SFam Zheng "ddb.adapterType = \"ide\"\n"; 1122*f66fd6c3SFam Zheng 1123*f66fd6c3SFam Zheng if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { 1124*f66fd6c3SFam Zheng return -EINVAL; 1125*f66fd6c3SFam Zheng } 1126*f66fd6c3SFam Zheng /* Read out options */ 1127*f66fd6c3SFam Zheng while (options && options->name) { 1128*f66fd6c3SFam Zheng if (!strcmp(options->name, BLOCK_OPT_SIZE)) { 1129*f66fd6c3SFam Zheng total_size = options->value.n; 1130*f66fd6c3SFam Zheng } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { 1131*f66fd6c3SFam Zheng backing_file = options->value.s; 1132*f66fd6c3SFam Zheng } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { 1133*f66fd6c3SFam Zheng flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0; 1134*f66fd6c3SFam Zheng } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) { 1135*f66fd6c3SFam Zheng fmt = options->value.s; 1136*f66fd6c3SFam Zheng } 1137*f66fd6c3SFam Zheng options++; 1138*f66fd6c3SFam Zheng } 1139*f66fd6c3SFam Zheng if (!fmt) { 1140*f66fd6c3SFam Zheng /* Default format to monolithicSparse */ 1141*f66fd6c3SFam Zheng fmt = "monolithicSparse"; 1142*f66fd6c3SFam Zheng } else if (strcmp(fmt, "monolithicFlat") && 1143*f66fd6c3SFam Zheng strcmp(fmt, "monolithicSparse") && 1144*f66fd6c3SFam Zheng strcmp(fmt, "twoGbMaxExtentSparse") && 1145*f66fd6c3SFam Zheng strcmp(fmt, "twoGbMaxExtentFlat")) { 1146*f66fd6c3SFam Zheng fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt); 1147*f66fd6c3SFam Zheng return -EINVAL; 1148*f66fd6c3SFam Zheng } 1149*f66fd6c3SFam Zheng split = !(strcmp(fmt, "twoGbMaxExtentFlat") && 1150*f66fd6c3SFam Zheng strcmp(fmt, "twoGbMaxExtentSparse")); 1151*f66fd6c3SFam Zheng flat = !(strcmp(fmt, "monolithicFlat") && 1152*f66fd6c3SFam Zheng strcmp(fmt, "twoGbMaxExtentFlat")); 1153*f66fd6c3SFam Zheng if (flat) { 1154*f66fd6c3SFam Zheng desc_extent_line = "RW %lld FLAT \"%s\" 0\n"; 1155*f66fd6c3SFam Zheng } else { 1156*f66fd6c3SFam Zheng desc_extent_line = "RW %lld SPARSE \"%s\"\n"; 1157*f66fd6c3SFam Zheng } 1158*f66fd6c3SFam Zheng if (flat && backing_file) { 1159*f66fd6c3SFam Zheng /* not supporting backing file for flat image */ 1160*f66fd6c3SFam Zheng return -ENOTSUP; 1161*f66fd6c3SFam Zheng } 1162*f66fd6c3SFam Zheng if (backing_file) { 1163*f66fd6c3SFam Zheng char parent_filename[PATH_MAX]; 1164*f66fd6c3SFam Zheng BlockDriverState *bs = bdrv_new(""); 1165*f66fd6c3SFam Zheng ret = bdrv_open(bs, backing_file, 0, NULL); 1166*f66fd6c3SFam Zheng if (ret != 0) { 1167*f66fd6c3SFam Zheng bdrv_delete(bs); 1168*f66fd6c3SFam Zheng return ret; 1169*f66fd6c3SFam Zheng } 1170*f66fd6c3SFam Zheng if (strcmp(bs->drv->format_name, "vmdk")) { 1171*f66fd6c3SFam Zheng bdrv_delete(bs); 1172*f66fd6c3SFam Zheng return -EINVAL; 1173*f66fd6c3SFam Zheng } 1174*f66fd6c3SFam Zheng filesize = bdrv_getlength(bs); 1175*f66fd6c3SFam Zheng parent_cid = vmdk_read_cid(bs, 0); 1176*f66fd6c3SFam Zheng bdrv_delete(bs); 1177*f66fd6c3SFam Zheng relative_path(parent_filename, sizeof(parent_filename), 1178*f66fd6c3SFam Zheng filename, backing_file); 1179*f66fd6c3SFam Zheng snprintf(parent_desc_line, sizeof(parent_desc_line), 1180*f66fd6c3SFam Zheng "parentFileNameHint=\"%s\"", parent_filename); 1181*f66fd6c3SFam Zheng } 1182*f66fd6c3SFam Zheng 1183*f66fd6c3SFam Zheng /* Create extents */ 1184*f66fd6c3SFam Zheng filesize = total_size; 1185*f66fd6c3SFam Zheng while (filesize > 0) { 1186*f66fd6c3SFam Zheng char desc_line[BUF_SIZE]; 1187*f66fd6c3SFam Zheng char ext_filename[PATH_MAX]; 1188*f66fd6c3SFam Zheng char desc_filename[PATH_MAX]; 1189*f66fd6c3SFam Zheng int64_t size = filesize; 1190*f66fd6c3SFam Zheng 1191*f66fd6c3SFam Zheng if (split && size > split_size) { 1192*f66fd6c3SFam Zheng size = split_size; 1193*f66fd6c3SFam Zheng } 1194*f66fd6c3SFam Zheng if (split) { 1195*f66fd6c3SFam Zheng snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s", 1196*f66fd6c3SFam Zheng prefix, flat ? 'f' : 's', ++idx, postfix); 1197*f66fd6c3SFam Zheng } else if (flat) { 1198*f66fd6c3SFam Zheng snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s", 1199*f66fd6c3SFam Zheng prefix, postfix); 1200*f66fd6c3SFam Zheng } else { 1201*f66fd6c3SFam Zheng snprintf(desc_filename, sizeof(desc_filename), "%s%s", 1202*f66fd6c3SFam Zheng prefix, postfix); 1203*f66fd6c3SFam Zheng } 1204*f66fd6c3SFam Zheng snprintf(ext_filename, sizeof(ext_filename), "%s%s", 1205*f66fd6c3SFam Zheng path, desc_filename); 1206*f66fd6c3SFam Zheng 1207*f66fd6c3SFam Zheng if (vmdk_create_extent(ext_filename, size, flat)) { 1208*f66fd6c3SFam Zheng return -EINVAL; 1209*f66fd6c3SFam Zheng } 1210*f66fd6c3SFam Zheng filesize -= size; 1211*f66fd6c3SFam Zheng 1212*f66fd6c3SFam Zheng /* Format description line */ 1213*f66fd6c3SFam Zheng snprintf(desc_line, sizeof(desc_line), 1214*f66fd6c3SFam Zheng desc_extent_line, size / 512, desc_filename); 1215*f66fd6c3SFam Zheng pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line); 1216*f66fd6c3SFam Zheng } 1217*f66fd6c3SFam Zheng /* generate descriptor file */ 1218*f66fd6c3SFam Zheng snprintf(desc, sizeof(desc), desc_template, 1219*f66fd6c3SFam Zheng (unsigned int)time(NULL), 1220*f66fd6c3SFam Zheng parent_cid, 1221*f66fd6c3SFam Zheng fmt, 1222*f66fd6c3SFam Zheng parent_desc_line, 1223*f66fd6c3SFam Zheng ext_desc_lines, 1224*f66fd6c3SFam Zheng (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), 1225*f66fd6c3SFam Zheng total_size / (int64_t)(63 * 16 * 512)); 1226*f66fd6c3SFam Zheng if (split || flat) { 1227*f66fd6c3SFam Zheng fd = open( 1228*f66fd6c3SFam Zheng filename, 1229*f66fd6c3SFam Zheng O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 1230*f66fd6c3SFam Zheng 0644); 1231*f66fd6c3SFam Zheng } else { 1232*f66fd6c3SFam Zheng fd = open( 1233*f66fd6c3SFam Zheng filename, 1234*f66fd6c3SFam Zheng O_WRONLY | O_BINARY | O_LARGEFILE, 1235*f66fd6c3SFam Zheng 0644); 1236*f66fd6c3SFam Zheng } 1237*f66fd6c3SFam Zheng if (fd < 0) { 1238*f66fd6c3SFam Zheng return -errno; 1239*f66fd6c3SFam Zheng } 1240*f66fd6c3SFam Zheng /* the descriptor offset = 0x200 */ 1241*f66fd6c3SFam Zheng if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) { 1242*f66fd6c3SFam Zheng ret = -errno; 1243*f66fd6c3SFam Zheng goto exit; 1244*f66fd6c3SFam Zheng } 12451640366cSKirill A. Shutemov ret = qemu_write_full(fd, desc, strlen(desc)); 12461640366cSKirill A. Shutemov if (ret != strlen(desc)) { 1247b781cce5SJuan Quintela ret = -errno; 12481640366cSKirill A. Shutemov goto exit; 12491640366cSKirill A. Shutemov } 12501640366cSKirill A. Shutemov ret = 0; 12511640366cSKirill A. Shutemov exit: 1252019d6b8fSAnthony Liguori close(fd); 12531640366cSKirill A. Shutemov return ret; 1254019d6b8fSAnthony Liguori } 1255019d6b8fSAnthony Liguori 1256019d6b8fSAnthony Liguori static void vmdk_close(BlockDriverState *bs) 1257019d6b8fSAnthony Liguori { 1258b3976d3cSFam Zheng vmdk_free_extents(bs); 1259019d6b8fSAnthony Liguori } 1260019d6b8fSAnthony Liguori 1261205ef796SKevin Wolf static int vmdk_flush(BlockDriverState *bs) 1262019d6b8fSAnthony Liguori { 1263333c574dSFam Zheng int i, ret, err; 1264333c574dSFam Zheng BDRVVmdkState *s = bs->opaque; 1265333c574dSFam Zheng 1266333c574dSFam Zheng ret = bdrv_flush(bs->file); 1267333c574dSFam Zheng for (i = 0; i < s->num_extents; i++) { 1268333c574dSFam Zheng err = bdrv_flush(s->extents[i].file); 1269333c574dSFam Zheng if (err < 0) { 1270333c574dSFam Zheng ret = err; 1271333c574dSFam Zheng } 1272333c574dSFam Zheng } 1273333c574dSFam Zheng return ret; 1274019d6b8fSAnthony Liguori } 1275019d6b8fSAnthony Liguori 12760e7e1989SKevin Wolf 12770e7e1989SKevin Wolf static QEMUOptionParameter vmdk_create_options[] = { 1278db08adf5SKevin Wolf { 1279db08adf5SKevin Wolf .name = BLOCK_OPT_SIZE, 1280db08adf5SKevin Wolf .type = OPT_SIZE, 1281db08adf5SKevin Wolf .help = "Virtual disk size" 1282db08adf5SKevin Wolf }, 1283db08adf5SKevin Wolf { 1284db08adf5SKevin Wolf .name = BLOCK_OPT_BACKING_FILE, 1285db08adf5SKevin Wolf .type = OPT_STRING, 1286db08adf5SKevin Wolf .help = "File name of a base image" 1287db08adf5SKevin Wolf }, 1288db08adf5SKevin Wolf { 1289db08adf5SKevin Wolf .name = BLOCK_OPT_COMPAT6, 1290db08adf5SKevin Wolf .type = OPT_FLAG, 1291db08adf5SKevin Wolf .help = "VMDK version 6 image" 1292db08adf5SKevin Wolf }, 1293*f66fd6c3SFam Zheng { 1294*f66fd6c3SFam Zheng .name = BLOCK_OPT_SUBFMT, 1295*f66fd6c3SFam Zheng .type = OPT_STRING, 1296*f66fd6c3SFam Zheng .help = 1297*f66fd6c3SFam Zheng "VMDK flat extent format, can be one of " 1298*f66fd6c3SFam Zheng "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} " 1299*f66fd6c3SFam Zheng }, 13000e7e1989SKevin Wolf { NULL } 13010e7e1989SKevin Wolf }; 13020e7e1989SKevin Wolf 1303019d6b8fSAnthony Liguori static BlockDriver bdrv_vmdk = { 1304019d6b8fSAnthony Liguori .format_name = "vmdk", 1305019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVVmdkState), 1306019d6b8fSAnthony Liguori .bdrv_probe = vmdk_probe, 13076511ef77SKevin Wolf .bdrv_open = vmdk_open, 1308019d6b8fSAnthony Liguori .bdrv_read = vmdk_read, 1309019d6b8fSAnthony Liguori .bdrv_write = vmdk_write, 1310019d6b8fSAnthony Liguori .bdrv_close = vmdk_close, 1311019d6b8fSAnthony Liguori .bdrv_create = vmdk_create, 1312019d6b8fSAnthony Liguori .bdrv_flush = vmdk_flush, 1313019d6b8fSAnthony Liguori .bdrv_is_allocated = vmdk_is_allocated, 13140e7e1989SKevin Wolf 13150e7e1989SKevin Wolf .create_options = vmdk_create_options, 1316019d6b8fSAnthony Liguori }; 1317019d6b8fSAnthony Liguori 1318019d6b8fSAnthony Liguori static void bdrv_vmdk_init(void) 1319019d6b8fSAnthony Liguori { 1320019d6b8fSAnthony Liguori bdrv_register(&bdrv_vmdk); 1321019d6b8fSAnthony Liguori } 1322019d6b8fSAnthony Liguori 1323019d6b8fSAnthony Liguori block_init(bdrv_vmdk_init); 1324