xref: /openbmc/qemu/block/vmdk.c (revision f66fd6c3)
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, &sectors, 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