|
|
1.1 root 1: /*
2: * Block driver for the VMDK format
3: *
4: * Copyright (c) 2004 Fabrice Bellard
5: * Copyright (c) 2005 Filip Navara
6: *
7: * Permission is hereby granted, free of charge, to any person obtaining a copy
8: * of this software and associated documentation files (the "Software"), to deal
9: * in the Software without restriction, including without limitation the rights
10: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11: * copies of the Software, and to permit persons to whom the Software is
12: * furnished to do so, subject to the following conditions:
13: *
14: * The above copyright notice and this permission notice shall be included in
15: * all copies or substantial portions of the Software.
16: *
17: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23: * THE SOFTWARE.
24: */
25:
26: #include "qemu-common.h"
27: #include "block_int.h"
28: #include "module.h"
29:
30: #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
31: #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
32:
33: typedef struct {
34: uint32_t version;
35: uint32_t flags;
36: uint32_t disk_sectors;
37: uint32_t granularity;
38: uint32_t l1dir_offset;
39: uint32_t l1dir_size;
40: uint32_t file_sectors;
41: uint32_t cylinders;
42: uint32_t heads;
43: uint32_t sectors_per_track;
44: } VMDK3Header;
45:
46: typedef struct {
47: uint32_t version;
48: uint32_t flags;
49: int64_t capacity;
50: int64_t granularity;
51: int64_t desc_offset;
52: int64_t desc_size;
53: int32_t num_gtes_per_gte;
54: int64_t rgd_offset;
55: int64_t gd_offset;
56: int64_t grain_offset;
57: char filler[1];
58: char check_bytes[4];
59: } __attribute__((packed)) VMDK4Header;
60:
61: #define L2_CACHE_SIZE 16
62:
1.1.1.5 ! root 63: typedef struct VmdkExtent {
! 64: BlockDriverState *file;
! 65: bool flat;
! 66: int64_t sectors;
! 67: int64_t end_sector;
! 68: int64_t flat_start_offset;
1.1 root 69: int64_t l1_table_offset;
70: int64_t l1_backup_table_offset;
71: uint32_t *l1_table;
72: uint32_t *l1_backup_table;
73: unsigned int l1_size;
74: uint32_t l1_entry_sectors;
75:
76: unsigned int l2_size;
77: uint32_t *l2_cache;
78: uint32_t l2_cache_offsets[L2_CACHE_SIZE];
79: uint32_t l2_cache_counts[L2_CACHE_SIZE];
80:
81: unsigned int cluster_sectors;
1.1.1.5 ! root 82: } VmdkExtent;
! 83:
! 84: typedef struct BDRVVmdkState {
! 85: int desc_offset;
! 86: bool cid_updated;
1.1 root 87: uint32_t parent_cid;
1.1.1.5 ! root 88: int num_extents;
! 89: /* Extent array with num_extents entries, ascend ordered by address */
! 90: VmdkExtent *extents;
1.1 root 91: } BDRVVmdkState;
92:
93: typedef struct VmdkMetaData {
94: uint32_t offset;
95: unsigned int l1_index;
96: unsigned int l2_index;
97: unsigned int l2_offset;
98: int valid;
99: } VmdkMetaData;
100:
101: static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
102: {
103: uint32_t magic;
104:
1.1.1.5 ! root 105: if (buf_size < 4) {
1.1 root 106: return 0;
1.1.1.5 ! root 107: }
1.1 root 108: magic = be32_to_cpu(*(uint32_t *)buf);
109: if (magic == VMDK3_MAGIC ||
1.1.1.5 ! root 110: magic == VMDK4_MAGIC) {
1.1 root 111: return 100;
1.1.1.5 ! root 112: } else {
! 113: const char *p = (const char *)buf;
! 114: const char *end = p + buf_size;
! 115: while (p < end) {
! 116: if (*p == '#') {
! 117: /* skip comment line */
! 118: while (p < end && *p != '\n') {
! 119: p++;
! 120: }
! 121: p++;
! 122: continue;
! 123: }
! 124: if (*p == ' ') {
! 125: while (p < end && *p == ' ') {
! 126: p++;
! 127: }
! 128: /* skip '\r' if windows line endings used. */
! 129: if (p < end && *p == '\r') {
! 130: p++;
! 131: }
! 132: /* only accept blank lines before 'version=' line */
! 133: if (p == end || *p != '\n') {
! 134: return 0;
! 135: }
! 136: p++;
! 137: continue;
! 138: }
! 139: if (end - p >= strlen("version=X\n")) {
! 140: if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 ||
! 141: strncmp("version=2\n", p, strlen("version=2\n")) == 0) {
! 142: return 100;
! 143: }
! 144: }
! 145: if (end - p >= strlen("version=X\r\n")) {
! 146: if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 ||
! 147: strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) {
! 148: return 100;
! 149: }
! 150: }
! 151: return 0;
! 152: }
1.1 root 153: return 0;
1.1.1.5 ! root 154: }
1.1 root 155: }
156:
157: #define CHECK_CID 1
158:
159: #define SECTOR_SIZE 512
1.1.1.5 ! root 160: #define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */
! 161: #define BUF_SIZE 4096
! 162: #define HEADER_SIZE 512 /* first sector of 512 bytes */
! 163:
! 164: static void vmdk_free_extents(BlockDriverState *bs)
! 165: {
! 166: int i;
! 167: BDRVVmdkState *s = bs->opaque;
! 168:
! 169: for (i = 0; i < s->num_extents; i++) {
! 170: qemu_free(s->extents[i].l1_table);
! 171: qemu_free(s->extents[i].l2_cache);
! 172: qemu_free(s->extents[i].l1_backup_table);
! 173: }
! 174: qemu_free(s->extents);
! 175: }
1.1 root 176:
177: static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
178: {
179: char desc[DESC_SIZE];
180: uint32_t cid;
181: const char *p_name, *cid_str;
182: size_t cid_str_size;
1.1.1.5 ! root 183: BDRVVmdkState *s = bs->opaque;
1.1 root 184:
1.1.1.5 ! root 185: if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
1.1 root 186: return 0;
1.1.1.5 ! root 187: }
1.1 root 188:
189: if (parent) {
190: cid_str = "parentCID";
191: cid_str_size = sizeof("parentCID");
192: } else {
193: cid_str = "CID";
194: cid_str_size = sizeof("CID");
195: }
196:
1.1.1.5 ! root 197: p_name = strstr(desc, cid_str);
! 198: if (p_name != NULL) {
1.1 root 199: p_name += cid_str_size;
1.1.1.5 ! root 200: sscanf(p_name, "%x", &cid);
1.1 root 201: }
202:
203: return cid;
204: }
205:
206: static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
207: {
208: char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
209: char *p_name, *tmp_str;
1.1.1.5 ! root 210: BDRVVmdkState *s = bs->opaque;
1.1 root 211:
1.1.1.5 ! root 212: memset(desc, 0, sizeof(desc));
! 213: if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
! 214: return -EIO;
! 215: }
1.1 root 216:
1.1.1.5 ! root 217: tmp_str = strstr(desc, "parentCID");
1.1 root 218: pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
1.1.1.5 ! root 219: p_name = strstr(desc, "CID");
! 220: if (p_name != NULL) {
1.1 root 221: p_name += sizeof("CID");
222: snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
223: pstrcat(desc, sizeof(desc), tmp_desc);
224: }
225:
1.1.1.5 ! root 226: if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) {
! 227: return -EIO;
! 228: }
1.1 root 229: return 0;
230: }
231:
232: static int vmdk_is_cid_valid(BlockDriverState *bs)
233: {
234: #ifdef CHECK_CID
235: BDRVVmdkState *s = bs->opaque;
236: BlockDriverState *p_bs = bs->backing_hd;
237: uint32_t cur_pcid;
238:
239: if (p_bs) {
1.1.1.5 ! root 240: cur_pcid = vmdk_read_cid(p_bs, 0);
! 241: if (s->parent_cid != cur_pcid) {
! 242: /* CID not valid */
1.1 root 243: return 0;
1.1.1.5 ! root 244: }
1.1 root 245: }
246: #endif
1.1.1.5 ! root 247: /* CID valid */
1.1 root 248: return 1;
249: }
250:
1.1.1.5 ! root 251: static int vmdk_parent_open(BlockDriverState *bs)
1.1 root 252: {
1.1.1.5 ! root 253: char *p_name;
! 254: char desc[DESC_SIZE + 1];
! 255: BDRVVmdkState *s = bs->opaque;
1.1 root 256:
1.1.1.5 ! root 257: desc[DESC_SIZE] = '\0';
! 258: if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
! 259: return -1;
1.1 root 260: }
261:
1.1.1.5 ! root 262: p_name = strstr(desc, "parentFileNameHint");
! 263: if (p_name != NULL) {
! 264: char *end_name;
1.1 root 265:
1.1.1.5 ! root 266: p_name += sizeof("parentFileNameHint") + 1;
! 267: end_name = strchr(p_name, '\"');
! 268: if (end_name == NULL) {
! 269: return -1;
! 270: }
! 271: if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
! 272: return -1;
! 273: }
! 274:
! 275: pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
1.1.1.3 root 276: }
1.1 root 277:
1.1.1.5 ! root 278: return 0;
! 279: }
1.1 root 280:
1.1.1.5 ! root 281: /* Create and append extent to the extent array. Return the added VmdkExtent
! 282: * address. return NULL if allocation failed. */
! 283: static VmdkExtent *vmdk_add_extent(BlockDriverState *bs,
! 284: BlockDriverState *file, bool flat, int64_t sectors,
! 285: int64_t l1_offset, int64_t l1_backup_offset,
! 286: uint32_t l1_size,
! 287: int l2_size, unsigned int cluster_sectors)
! 288: {
! 289: VmdkExtent *extent;
! 290: BDRVVmdkState *s = bs->opaque;
! 291:
! 292: s->extents = qemu_realloc(s->extents,
! 293: (s->num_extents + 1) * sizeof(VmdkExtent));
! 294: extent = &s->extents[s->num_extents];
! 295: s->num_extents++;
! 296:
! 297: memset(extent, 0, sizeof(VmdkExtent));
! 298: extent->file = file;
! 299: extent->flat = flat;
! 300: extent->sectors = sectors;
! 301: extent->l1_table_offset = l1_offset;
! 302: extent->l1_backup_table_offset = l1_backup_offset;
! 303: extent->l1_size = l1_size;
! 304: extent->l1_entry_sectors = l2_size * cluster_sectors;
! 305: extent->l2_size = l2_size;
! 306: extent->cluster_sectors = cluster_sectors;
! 307:
! 308: if (s->num_extents > 1) {
! 309: extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
! 310: } else {
! 311: extent->end_sector = extent->sectors;
1.1.1.3 root 312: }
1.1.1.5 ! root 313: bs->total_sectors = extent->end_sector;
! 314: return extent;
! 315: }
! 316:
! 317: static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
! 318: {
! 319: int ret;
! 320: int l1_size, i;
! 321:
! 322: /* read the L1 table */
! 323: l1_size = extent->l1_size * sizeof(uint32_t);
! 324: extent->l1_table = qemu_malloc(l1_size);
! 325: ret = bdrv_pread(extent->file,
! 326: extent->l1_table_offset,
! 327: extent->l1_table,
! 328: l1_size);
! 329: if (ret < 0) {
! 330: goto fail_l1;
1.1.1.3 root 331: }
1.1.1.5 ! root 332: for (i = 0; i < extent->l1_size; i++) {
! 333: le32_to_cpus(&extent->l1_table[i]);
1.1.1.3 root 334: }
1.1 root 335:
1.1.1.5 ! root 336: if (extent->l1_backup_table_offset) {
! 337: extent->l1_backup_table = qemu_malloc(l1_size);
! 338: ret = bdrv_pread(extent->file,
! 339: extent->l1_backup_table_offset,
! 340: extent->l1_backup_table,
! 341: l1_size);
! 342: if (ret < 0) {
! 343: goto fail_l1b;
! 344: }
! 345: for (i = 0; i < extent->l1_size; i++) {
! 346: le32_to_cpus(&extent->l1_backup_table[i]);
! 347: }
1.1 root 348: }
349:
1.1.1.5 ! root 350: extent->l2_cache =
! 351: qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
! 352: return 0;
! 353: fail_l1b:
! 354: qemu_free(extent->l1_backup_table);
! 355: fail_l1:
! 356: qemu_free(extent->l1_table);
! 357: return ret;
! 358: }
1.1 root 359:
1.1.1.5 ! root 360: static int vmdk_open_vmdk3(BlockDriverState *bs, int flags)
! 361: {
! 362: int ret;
! 363: uint32_t magic;
! 364: VMDK3Header header;
! 365: BDRVVmdkState *s = bs->opaque;
! 366: VmdkExtent *extent;
1.1 root 367:
1.1.1.5 ! root 368: s->desc_offset = 0x200;
! 369: ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header));
! 370: if (ret < 0) {
1.1 root 371: goto fail;
1.1.1.3 root 372: }
1.1.1.5 ! root 373: extent = vmdk_add_extent(bs,
! 374: bs->file, false,
! 375: le32_to_cpu(header.disk_sectors),
! 376: le32_to_cpu(header.l1dir_offset) << 9,
! 377: 0, 1 << 6, 1 << 9,
! 378: le32_to_cpu(header.granularity));
! 379: ret = vmdk_init_tables(bs, extent);
! 380: if (ret) {
! 381: /* vmdk_init_tables cleans up on fail, so only free allocation of
! 382: * vmdk_add_extent here. */
1.1 root 383: goto fail;
1.1.1.3 root 384: }
1.1.1.5 ! root 385: return 0;
! 386: fail:
! 387: vmdk_free_extents(bs);
! 388: return ret;
! 389: }
1.1 root 390:
1.1.1.5 ! root 391: static int vmdk_open_vmdk4(BlockDriverState *bs, int flags)
! 392: {
! 393: int ret;
! 394: uint32_t magic;
! 395: uint32_t l1_size, l1_entry_sectors;
! 396: VMDK4Header header;
! 397: BDRVVmdkState *s = bs->opaque;
! 398: VmdkExtent *extent;
1.1 root 399:
1.1.1.5 ! root 400: s->desc_offset = 0x200;
! 401: ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header));
! 402: if (ret < 0) {
! 403: goto fail;
1.1.1.3 root 404: }
1.1.1.5 ! root 405: l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte)
! 406: * le64_to_cpu(header.granularity);
! 407: l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)
! 408: / l1_entry_sectors;
! 409: extent = vmdk_add_extent(bs, bs->file, false,
! 410: le64_to_cpu(header.capacity),
! 411: le64_to_cpu(header.gd_offset) << 9,
! 412: le64_to_cpu(header.rgd_offset) << 9,
! 413: l1_size,
! 414: le32_to_cpu(header.num_gtes_per_gte),
! 415: le64_to_cpu(header.granularity));
! 416: if (extent->l1_entry_sectors <= 0) {
! 417: ret = -EINVAL;
! 418: goto fail;
1.1.1.3 root 419: }
1.1.1.5 ! root 420: /* try to open parent images, if exist */
! 421: ret = vmdk_parent_open(bs);
! 422: if (ret) {
! 423: goto fail;
1.1.1.3 root 424: }
1.1.1.5 ! root 425: s->parent_cid = vmdk_read_cid(bs, 1);
! 426: ret = vmdk_init_tables(bs, extent);
! 427: if (ret) {
! 428: goto fail;
1.1.1.3 root 429: }
1.1.1.5 ! root 430: return 0;
! 431: fail:
! 432: vmdk_free_extents(bs);
! 433: return ret;
! 434: }
1.1 root 435:
1.1.1.5 ! root 436: /* find an option value out of descriptor file */
! 437: static int vmdk_parse_description(const char *desc, const char *opt_name,
! 438: char *buf, int buf_size)
! 439: {
! 440: char *opt_pos, *opt_end;
! 441: const char *end = desc + strlen(desc);
! 442:
! 443: opt_pos = strstr(desc, opt_name);
! 444: if (!opt_pos) {
! 445: return -1;
1.1.1.3 root 446: }
1.1.1.5 ! root 447: /* Skip "=\"" following opt_name */
! 448: opt_pos += strlen(opt_name) + 2;
! 449: if (opt_pos >= end) {
! 450: return -1;
1.1.1.3 root 451: }
1.1.1.5 ! root 452: opt_end = opt_pos;
! 453: while (opt_end < end && *opt_end != '"') {
! 454: opt_end++;
1.1.1.3 root 455: }
1.1.1.5 ! root 456: if (opt_end == end || buf_size < opt_end - opt_pos + 1) {
! 457: return -1;
1.1.1.3 root 458: }
1.1.1.5 ! root 459: pstrcpy(buf, opt_end - opt_pos + 1, opt_pos);
! 460: return 0;
1.1 root 461: }
462:
1.1.1.5 ! root 463: static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
! 464: const char *desc_file_path)
1.1 root 465: {
1.1.1.5 ! root 466: int ret;
! 467: char access[11];
! 468: char type[11];
! 469: char fname[512];
! 470: const char *p = desc;
! 471: int64_t sectors = 0;
! 472: int64_t flat_offset;
! 473:
! 474: while (*p) {
! 475: /* parse extent line:
! 476: * RW [size in sectors] FLAT "file-name.vmdk" OFFSET
! 477: * or
! 478: * RW [size in sectors] SPARSE "file-name.vmdk"
! 479: */
! 480: flat_offset = -1;
! 481: ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64,
! 482: access, §ors, type, fname, &flat_offset);
! 483: if (ret < 4 || strcmp(access, "RW")) {
! 484: goto next_line;
! 485: } else if (!strcmp(type, "FLAT")) {
! 486: if (ret != 5 || flat_offset < 0) {
! 487: return -EINVAL;
! 488: }
! 489: } else if (ret != 4) {
! 490: return -EINVAL;
! 491: }
1.1 root 492:
1.1.1.5 ! root 493: /* trim the quotation marks around */
! 494: if (fname[0] == '"') {
! 495: memmove(fname, fname + 1, strlen(fname));
! 496: if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') {
! 497: return -EINVAL;
! 498: }
! 499: fname[strlen(fname) - 1] = '\0';
! 500: }
! 501: if (sectors <= 0 ||
! 502: (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) ||
! 503: (strcmp(access, "RW"))) {
! 504: goto next_line;
! 505: }
1.1 root 506:
1.1.1.5 ! root 507: /* save to extents array */
! 508: if (!strcmp(type, "FLAT")) {
! 509: /* FLAT extent */
! 510: char extent_path[PATH_MAX];
! 511: BlockDriverState *extent_file;
! 512: VmdkExtent *extent;
! 513:
! 514: path_combine(extent_path, sizeof(extent_path),
! 515: desc_file_path, fname);
! 516: ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags);
! 517: if (ret) {
! 518: return ret;
! 519: }
! 520: extent = vmdk_add_extent(bs, extent_file, true, sectors,
! 521: 0, 0, 0, 0, sectors);
! 522: extent->flat_start_offset = flat_offset;
! 523: } else {
! 524: /* SPARSE extent, not supported for now */
! 525: fprintf(stderr,
! 526: "VMDK: Not supported extent type \"%s\""".\n", type);
! 527: return -ENOTSUP;
! 528: }
! 529: next_line:
! 530: /* move to next line */
! 531: while (*p && *p != '\n') {
! 532: p++;
! 533: }
! 534: p++;
! 535: }
! 536: return 0;
! 537: }
1.1 root 538:
1.1.1.5 ! root 539: static int vmdk_open_desc_file(BlockDriverState *bs, int flags)
! 540: {
! 541: int ret;
! 542: char buf[2048];
! 543: char ct[128];
! 544: BDRVVmdkState *s = bs->opaque;
1.1 root 545:
1.1.1.5 ! root 546: ret = bdrv_pread(bs->file, 0, buf, sizeof(buf));
! 547: if (ret < 0) {
! 548: return ret;
! 549: }
! 550: buf[2047] = '\0';
! 551: if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
! 552: return -EINVAL;
! 553: }
! 554: if (strcmp(ct, "monolithicFlat")) {
! 555: fprintf(stderr,
! 556: "VMDK: Not supported image type \"%s\""".\n", ct);
! 557: return -ENOTSUP;
! 558: }
! 559: s->desc_offset = 0;
! 560: ret = vmdk_parse_extents(buf, bs, bs->file->filename);
! 561: if (ret) {
! 562: return ret;
1.1 root 563: }
564:
1.1.1.5 ! root 565: /* try to open parent images, if exist */
! 566: if (vmdk_parent_open(bs)) {
! 567: qemu_free(s->extents);
! 568: return -EINVAL;
! 569: }
! 570: s->parent_cid = vmdk_read_cid(bs, 1);
1.1 root 571: return 0;
572: }
573:
1.1.1.3 root 574: static int vmdk_open(BlockDriverState *bs, int flags)
1.1 root 575: {
576: uint32_t magic;
577:
1.1.1.5 ! root 578: if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) {
! 579: return -EIO;
! 580: }
1.1 root 581:
582: magic = be32_to_cpu(magic);
583: if (magic == VMDK3_MAGIC) {
1.1.1.5 ! root 584: return vmdk_open_vmdk3(bs, flags);
1.1 root 585: } else if (magic == VMDK4_MAGIC) {
1.1.1.5 ! root 586: return vmdk_open_vmdk4(bs, flags);
1.1 root 587: } else {
1.1.1.5 ! root 588: return vmdk_open_desc_file(bs, flags);
1.1 root 589: }
590: }
591:
1.1.1.5 ! root 592: static int get_whole_cluster(BlockDriverState *bs,
! 593: VmdkExtent *extent,
! 594: uint64_t cluster_offset,
! 595: uint64_t offset,
! 596: bool allocate)
1.1 root 597: {
1.1.1.5 ! root 598: /* 128 sectors * 512 bytes each = grain size 64KB */
! 599: uint8_t whole_grain[extent->cluster_sectors * 512];
1.1 root 600:
1.1.1.5 ! root 601: /* we will be here if it's first write on non-exist grain(cluster).
! 602: * try to read from parent image, if exist */
1.1 root 603: if (bs->backing_hd) {
1.1.1.2 root 604: int ret;
1.1 root 605:
1.1.1.5 ! root 606: if (!vmdk_is_cid_valid(bs)) {
1.1 root 607: return -1;
1.1.1.5 ! root 608: }
1.1 root 609:
1.1.1.5 ! root 610: /* floor offset to cluster */
! 611: offset -= offset % (extent->cluster_sectors * 512);
1.1.1.2 root 612: ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain,
1.1.1.5 ! root 613: extent->cluster_sectors);
1.1.1.2 root 614: if (ret < 0) {
615: return -1;
616: }
1.1 root 617:
1.1.1.5 ! root 618: /* Write grain only into the active image */
! 619: ret = bdrv_write(extent->file, cluster_offset, whole_grain,
! 620: extent->cluster_sectors);
1.1.1.2 root 621: if (ret < 0) {
622: return -1;
1.1 root 623: }
624: }
625: return 0;
626: }
627:
1.1.1.5 ! root 628: static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data)
1.1 root 629: {
630: /* update L2 table */
1.1.1.5 ! root 631: if (bdrv_pwrite_sync(
! 632: extent->file,
! 633: ((int64_t)m_data->l2_offset * 512)
! 634: + (m_data->l2_index * sizeof(m_data->offset)),
! 635: &(m_data->offset),
! 636: sizeof(m_data->offset)
! 637: ) < 0) {
1.1 root 638: return -1;
1.1.1.5 ! root 639: }
1.1 root 640: /* update backup L2 table */
1.1.1.5 ! root 641: if (extent->l1_backup_table_offset != 0) {
! 642: m_data->l2_offset = extent->l1_backup_table[m_data->l1_index];
! 643: if (bdrv_pwrite_sync(
! 644: extent->file,
! 645: ((int64_t)m_data->l2_offset * 512)
! 646: + (m_data->l2_index * sizeof(m_data->offset)),
! 647: &(m_data->offset), sizeof(m_data->offset)
! 648: ) < 0) {
1.1 root 649: return -1;
1.1.1.5 ! root 650: }
1.1 root 651: }
652:
653: return 0;
654: }
655:
1.1.1.5 ! root 656: static int get_cluster_offset(BlockDriverState *bs,
! 657: VmdkExtent *extent,
! 658: VmdkMetaData *m_data,
! 659: uint64_t offset,
! 660: int allocate,
! 661: uint64_t *cluster_offset)
1.1 root 662: {
663: unsigned int l1_index, l2_offset, l2_index;
664: int min_index, i, j;
665: uint32_t min_count, *l2_table, tmp = 0;
666:
1.1.1.5 ! root 667: if (m_data) {
1.1 root 668: m_data->valid = 0;
1.1.1.5 ! root 669: }
! 670: if (extent->flat) {
! 671: *cluster_offset = extent->flat_start_offset;
1.1 root 672: return 0;
1.1.1.5 ! root 673: }
! 674:
! 675: l1_index = (offset >> 9) / extent->l1_entry_sectors;
! 676: if (l1_index >= extent->l1_size) {
! 677: return -1;
! 678: }
! 679: l2_offset = extent->l1_table[l1_index];
! 680: if (!l2_offset) {
! 681: return -1;
! 682: }
! 683: for (i = 0; i < L2_CACHE_SIZE; i++) {
! 684: if (l2_offset == extent->l2_cache_offsets[i]) {
1.1 root 685: /* increment the hit count */
1.1.1.5 ! root 686: if (++extent->l2_cache_counts[i] == 0xffffffff) {
! 687: for (j = 0; j < L2_CACHE_SIZE; j++) {
! 688: extent->l2_cache_counts[j] >>= 1;
1.1 root 689: }
690: }
1.1.1.5 ! root 691: l2_table = extent->l2_cache + (i * extent->l2_size);
1.1 root 692: goto found;
693: }
694: }
695: /* not found: load a new entry in the least used one */
696: min_index = 0;
697: min_count = 0xffffffff;
1.1.1.5 ! root 698: for (i = 0; i < L2_CACHE_SIZE; i++) {
! 699: if (extent->l2_cache_counts[i] < min_count) {
! 700: min_count = extent->l2_cache_counts[i];
1.1 root 701: min_index = i;
702: }
703: }
1.1.1.5 ! root 704: l2_table = extent->l2_cache + (min_index * extent->l2_size);
! 705: if (bdrv_pread(
! 706: extent->file,
! 707: (int64_t)l2_offset * 512,
! 708: l2_table,
! 709: extent->l2_size * sizeof(uint32_t)
! 710: ) != extent->l2_size * sizeof(uint32_t)) {
! 711: return -1;
! 712: }
1.1 root 713:
1.1.1.5 ! root 714: extent->l2_cache_offsets[min_index] = l2_offset;
! 715: extent->l2_cache_counts[min_index] = 1;
1.1 root 716: found:
1.1.1.5 ! root 717: l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
! 718: *cluster_offset = le32_to_cpu(l2_table[l2_index]);
1.1 root 719:
1.1.1.5 ! root 720: if (!*cluster_offset) {
! 721: if (!allocate) {
! 722: return -1;
! 723: }
1.1.1.3 root 724:
1.1.1.5 ! root 725: /* Avoid the L2 tables update for the images that have snapshots. */
! 726: *cluster_offset = bdrv_getlength(extent->file);
! 727: bdrv_truncate(
! 728: extent->file,
! 729: *cluster_offset + (extent->cluster_sectors << 9)
! 730: );
1.1.1.3 root 731:
1.1.1.5 ! root 732: *cluster_offset >>= 9;
! 733: tmp = cpu_to_le32(*cluster_offset);
1.1.1.3 root 734: l2_table[l2_index] = tmp;
735:
1.1 root 736: /* First of all we write grain itself, to avoid race condition
737: * that may to corrupt the image.
738: * This problem may occur because of insufficient space on host disk
739: * or inappropriate VM shutdown.
740: */
1.1.1.5 ! root 741: if (get_whole_cluster(
! 742: bs, extent, *cluster_offset, offset, allocate) == -1) {
! 743: return -1;
! 744: }
1.1 root 745:
746: if (m_data) {
747: m_data->offset = tmp;
748: m_data->l1_index = l1_index;
749: m_data->l2_index = l2_index;
750: m_data->l2_offset = l2_offset;
751: m_data->valid = 1;
752: }
753: }
1.1.1.5 ! root 754: *cluster_offset <<= 9;
! 755: return 0;
! 756: }
! 757:
! 758: static VmdkExtent *find_extent(BDRVVmdkState *s,
! 759: int64_t sector_num, VmdkExtent *start_hint)
! 760: {
! 761: VmdkExtent *extent = start_hint;
! 762:
! 763: if (!extent) {
! 764: extent = &s->extents[0];
! 765: }
! 766: while (extent < &s->extents[s->num_extents]) {
! 767: if (sector_num < extent->end_sector) {
! 768: return extent;
! 769: }
! 770: extent++;
! 771: }
! 772: return NULL;
1.1 root 773: }
774:
775: static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
776: int nb_sectors, int *pnum)
777: {
778: BDRVVmdkState *s = bs->opaque;
1.1.1.5 ! root 779: int64_t index_in_cluster, n, ret;
! 780: uint64_t offset;
! 781: VmdkExtent *extent;
1.1 root 782:
1.1.1.5 ! root 783: extent = find_extent(s, sector_num, NULL);
! 784: if (!extent) {
! 785: return 0;
! 786: }
! 787: ret = get_cluster_offset(bs, extent, NULL,
! 788: sector_num * 512, 0, &offset);
! 789: /* get_cluster_offset returning 0 means success */
! 790: ret = !ret;
! 791:
! 792: index_in_cluster = sector_num % extent->cluster_sectors;
! 793: n = extent->cluster_sectors - index_in_cluster;
! 794: if (n > nb_sectors) {
1.1 root 795: n = nb_sectors;
1.1.1.5 ! root 796: }
1.1 root 797: *pnum = n;
1.1.1.5 ! root 798: return ret;
1.1 root 799: }
800:
801: static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
802: uint8_t *buf, int nb_sectors)
803: {
804: BDRVVmdkState *s = bs->opaque;
1.1.1.5 ! root 805: int ret;
! 806: uint64_t n, index_in_cluster;
! 807: VmdkExtent *extent = NULL;
1.1 root 808: uint64_t cluster_offset;
809:
810: while (nb_sectors > 0) {
1.1.1.5 ! root 811: extent = find_extent(s, sector_num, extent);
! 812: if (!extent) {
! 813: return -EIO;
! 814: }
! 815: ret = get_cluster_offset(
! 816: bs, extent, NULL,
! 817: sector_num << 9, 0, &cluster_offset);
! 818: index_in_cluster = sector_num % extent->cluster_sectors;
! 819: n = extent->cluster_sectors - index_in_cluster;
! 820: if (n > nb_sectors) {
1.1 root 821: n = nb_sectors;
1.1.1.5 ! root 822: }
! 823: if (ret) {
! 824: /* if not allocated, try to read from parent image, if exist */
1.1 root 825: if (bs->backing_hd) {
1.1.1.5 ! root 826: if (!vmdk_is_cid_valid(bs)) {
! 827: return -EINVAL;
! 828: }
1.1 root 829: ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
1.1.1.5 ! root 830: if (ret < 0) {
! 831: return ret;
! 832: }
1.1 root 833: } else {
834: memset(buf, 0, 512 * n);
835: }
836: } else {
1.1.1.5 ! root 837: ret = bdrv_pread(extent->file,
! 838: cluster_offset + index_in_cluster * 512,
! 839: buf, n * 512);
! 840: if (ret < 0) {
! 841: return ret;
! 842: }
1.1 root 843: }
844: nb_sectors -= n;
845: sector_num += n;
846: buf += n * 512;
847: }
848: return 0;
849: }
850:
851: static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
852: const uint8_t *buf, int nb_sectors)
853: {
854: BDRVVmdkState *s = bs->opaque;
1.1.1.5 ! root 855: VmdkExtent *extent = NULL;
! 856: int n, ret;
! 857: int64_t index_in_cluster;
1.1 root 858: uint64_t cluster_offset;
1.1.1.5 ! root 859: VmdkMetaData m_data;
1.1 root 860:
861: if (sector_num > bs->total_sectors) {
862: fprintf(stderr,
863: "(VMDK) Wrong offset: sector_num=0x%" PRIx64
864: " total_sectors=0x%" PRIx64 "\n",
865: sector_num, bs->total_sectors);
1.1.1.5 ! root 866: return -EIO;
1.1 root 867: }
868:
869: while (nb_sectors > 0) {
1.1.1.5 ! root 870: extent = find_extent(s, sector_num, extent);
! 871: if (!extent) {
! 872: return -EIO;
! 873: }
! 874: ret = get_cluster_offset(
! 875: bs,
! 876: extent,
! 877: &m_data,
! 878: sector_num << 9, 1,
! 879: &cluster_offset);
! 880: if (ret) {
! 881: return -EINVAL;
! 882: }
! 883: index_in_cluster = sector_num % extent->cluster_sectors;
! 884: n = extent->cluster_sectors - index_in_cluster;
! 885: if (n > nb_sectors) {
1.1 root 886: n = nb_sectors;
1.1.1.5 ! root 887: }
1.1 root 888:
1.1.1.5 ! root 889: ret = bdrv_pwrite(extent->file,
! 890: cluster_offset + index_in_cluster * 512,
! 891: buf,
! 892: n * 512);
! 893: if (ret < 0) {
! 894: return ret;
! 895: }
1.1 root 896: if (m_data.valid) {
897: /* update L2 tables */
1.1.1.5 ! root 898: if (vmdk_L2update(extent, &m_data) == -1) {
! 899: return -EIO;
! 900: }
1.1 root 901: }
902: nb_sectors -= n;
903: sector_num += n;
904: buf += n * 512;
905:
1.1.1.5 ! root 906: /* update CID on the first write every time the virtual disk is
! 907: * opened */
! 908: if (!s->cid_updated) {
1.1 root 909: vmdk_write_cid(bs, time(NULL));
1.1.1.5 ! root 910: s->cid_updated = true;
1.1 root 911: }
912: }
913: return 0;
914: }
915:
1.1.1.5 ! root 916:
! 917: static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat)
1.1 root 918: {
1.1.1.5 ! root 919: int ret, i;
! 920: int fd = 0;
1.1 root 921: VMDK4Header header;
922: uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
923:
1.1.1.5 ! root 924: fd = open(
! 925: filename,
! 926: O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
! 927: 0644);
! 928: if (fd < 0) {
! 929: return -errno;
1.1 root 930: }
1.1.1.5 ! root 931: if (flat) {
! 932: ret = ftruncate(fd, filesize);
! 933: if (ret < 0) {
! 934: ret = -errno;
! 935: }
! 936: goto exit;
1.1 root 937: }
938: magic = cpu_to_be32(VMDK4_MAGIC);
939: memset(&header, 0, sizeof(header));
1.1.1.5 ! root 940: header.version = 1;
! 941: header.flags = 3; /* ?? */
! 942: header.capacity = filesize / 512;
! 943: header.granularity = 128;
! 944: header.num_gtes_per_gte = 512;
1.1 root 945:
1.1.1.5 ! root 946: grains = (filesize / 512 + header.granularity - 1) / header.granularity;
1.1 root 947: gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
1.1.1.5 ! root 948: gt_count =
! 949: (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
1.1 root 950: gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
951:
952: header.desc_offset = 1;
953: header.desc_size = 20;
954: header.rgd_offset = header.desc_offset + header.desc_size;
955: header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
956: header.grain_offset =
957: ((header.gd_offset + gd_size + (gt_size * gt_count) +
958: header.granularity - 1) / header.granularity) *
959: header.granularity;
1.1.1.5 ! root 960: /* swap endianness for all header fields */
! 961: header.version = cpu_to_le32(header.version);
! 962: header.flags = cpu_to_le32(header.flags);
! 963: header.capacity = cpu_to_le64(header.capacity);
! 964: header.granularity = cpu_to_le64(header.granularity);
! 965: header.num_gtes_per_gte = cpu_to_le32(header.num_gtes_per_gte);
1.1 root 966: header.desc_offset = cpu_to_le64(header.desc_offset);
967: header.desc_size = cpu_to_le64(header.desc_size);
968: header.rgd_offset = cpu_to_le64(header.rgd_offset);
969: header.gd_offset = cpu_to_le64(header.gd_offset);
970: header.grain_offset = cpu_to_le64(header.grain_offset);
971:
972: header.check_bytes[0] = 0xa;
973: header.check_bytes[1] = 0x20;
974: header.check_bytes[2] = 0xd;
975: header.check_bytes[3] = 0xa;
976:
977: /* write all the data */
1.1.1.3 root 978: ret = qemu_write_full(fd, &magic, sizeof(magic));
979: if (ret != sizeof(magic)) {
980: ret = -errno;
981: goto exit;
982: }
983: ret = qemu_write_full(fd, &header, sizeof(header));
984: if (ret != sizeof(header)) {
985: ret = -errno;
986: goto exit;
987: }
1.1 root 988:
1.1.1.5 ! root 989: ret = ftruncate(fd, le64_to_cpu(header.grain_offset) << 9);
1.1.1.3 root 990: if (ret < 0) {
991: ret = -errno;
992: goto exit;
993: }
1.1 root 994:
995: /* write grain directory */
996: lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
1.1.1.5 ! root 997: for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_size;
1.1.1.3 root 998: i < gt_count; i++, tmp += gt_size) {
999: ret = qemu_write_full(fd, &tmp, sizeof(tmp));
1000: if (ret != sizeof(tmp)) {
1001: ret = -errno;
1002: goto exit;
1003: }
1004: }
1.1 root 1005:
1006: /* write backup grain directory */
1007: lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
1.1.1.5 ! root 1008: for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_size;
1.1.1.3 root 1009: i < gt_count; i++, tmp += gt_size) {
1010: ret = qemu_write_full(fd, &tmp, sizeof(tmp));
1011: if (ret != sizeof(tmp)) {
1012: ret = -errno;
1013: goto exit;
1014: }
1015: }
1.1 root 1016:
1.1.1.5 ! root 1017: ret = 0;
! 1018: exit:
! 1019: close(fd);
! 1020: return ret;
! 1021: }
! 1022:
! 1023: static int filename_decompose(const char *filename, char *path, char *prefix,
! 1024: char *postfix, size_t buf_len)
! 1025: {
! 1026: const char *p, *q;
! 1027:
! 1028: if (filename == NULL || !strlen(filename)) {
! 1029: fprintf(stderr, "Vmdk: no filename provided.\n");
! 1030: return -1;
! 1031: }
! 1032: p = strrchr(filename, '/');
! 1033: if (p == NULL) {
! 1034: p = strrchr(filename, '\\');
! 1035: }
! 1036: if (p == NULL) {
! 1037: p = strrchr(filename, ':');
! 1038: }
! 1039: if (p != NULL) {
! 1040: p++;
! 1041: if (p - filename >= buf_len) {
! 1042: return -1;
! 1043: }
! 1044: pstrcpy(path, p - filename + 1, filename);
! 1045: } else {
! 1046: p = filename;
! 1047: path[0] = '\0';
! 1048: }
! 1049: q = strrchr(p, '.');
! 1050: if (q == NULL) {
! 1051: pstrcpy(prefix, buf_len, p);
! 1052: postfix[0] = '\0';
! 1053: } else {
! 1054: if (q - p >= buf_len) {
! 1055: return -1;
! 1056: }
! 1057: pstrcpy(prefix, q - p + 1, p);
! 1058: pstrcpy(postfix, buf_len, q);
! 1059: }
! 1060: return 0;
! 1061: }
! 1062:
! 1063: static int relative_path(char *dest, int dest_size,
! 1064: const char *base, const char *target)
! 1065: {
! 1066: int i = 0;
! 1067: int n = 0;
! 1068: const char *p, *q;
! 1069: #ifdef _WIN32
! 1070: const char *sep = "\\";
! 1071: #else
! 1072: const char *sep = "/";
! 1073: #endif
! 1074:
! 1075: if (!(dest && base && target)) {
! 1076: return -1;
! 1077: }
! 1078: if (path_is_absolute(target)) {
! 1079: dest[dest_size - 1] = '\0';
! 1080: strncpy(dest, target, dest_size - 1);
! 1081: return 0;
! 1082: }
! 1083: while (base[i] == target[i]) {
! 1084: i++;
! 1085: }
! 1086: p = &base[i];
! 1087: q = &target[i];
! 1088: while (*p) {
! 1089: if (*p == *sep) {
! 1090: n++;
! 1091: }
! 1092: p++;
! 1093: }
! 1094: dest[0] = '\0';
! 1095: for (; n; n--) {
! 1096: pstrcat(dest, dest_size, "..");
! 1097: pstrcat(dest, dest_size, sep);
! 1098: }
! 1099: pstrcat(dest, dest_size, q);
! 1100: return 0;
! 1101: }
! 1102:
! 1103: static int vmdk_create(const char *filename, QEMUOptionParameter *options)
! 1104: {
! 1105: int fd, idx = 0;
! 1106: char desc[BUF_SIZE];
! 1107: int64_t total_size = 0, filesize;
! 1108: const char *backing_file = NULL;
! 1109: const char *fmt = NULL;
! 1110: int flags = 0;
! 1111: int ret = 0;
! 1112: bool flat, split;
! 1113: char ext_desc_lines[BUF_SIZE] = "";
! 1114: char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX];
! 1115: const int64_t split_size = 0x80000000; /* VMDK has constant split size */
! 1116: const char *desc_extent_line;
! 1117: char parent_desc_line[BUF_SIZE] = "";
! 1118: uint32_t parent_cid = 0xffffffff;
! 1119: const char desc_template[] =
! 1120: "# Disk DescriptorFile\n"
! 1121: "version=1\n"
! 1122: "CID=%x\n"
! 1123: "parentCID=%x\n"
! 1124: "createType=\"%s\"\n"
! 1125: "%s"
! 1126: "\n"
! 1127: "# Extent description\n"
! 1128: "%s"
! 1129: "\n"
! 1130: "# The Disk Data Base\n"
! 1131: "#DDB\n"
! 1132: "\n"
! 1133: "ddb.virtualHWVersion = \"%d\"\n"
! 1134: "ddb.geometry.cylinders = \"%" PRId64 "\"\n"
! 1135: "ddb.geometry.heads = \"16\"\n"
! 1136: "ddb.geometry.sectors = \"63\"\n"
! 1137: "ddb.adapterType = \"ide\"\n";
1.1 root 1138:
1.1.1.5 ! root 1139: if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
! 1140: return -EINVAL;
! 1141: }
! 1142: /* Read out options */
! 1143: while (options && options->name) {
! 1144: if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
! 1145: total_size = options->value.n;
! 1146: } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
! 1147: backing_file = options->value.s;
! 1148: } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
! 1149: flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0;
! 1150: } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
! 1151: fmt = options->value.s;
! 1152: }
! 1153: options++;
! 1154: }
! 1155: if (!fmt) {
! 1156: /* Default format to monolithicSparse */
! 1157: fmt = "monolithicSparse";
! 1158: } else if (strcmp(fmt, "monolithicFlat") &&
! 1159: strcmp(fmt, "monolithicSparse") &&
! 1160: strcmp(fmt, "twoGbMaxExtentSparse") &&
! 1161: strcmp(fmt, "twoGbMaxExtentFlat")) {
! 1162: fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt);
! 1163: return -EINVAL;
! 1164: }
! 1165: split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
! 1166: strcmp(fmt, "twoGbMaxExtentSparse"));
! 1167: flat = !(strcmp(fmt, "monolithicFlat") &&
! 1168: strcmp(fmt, "twoGbMaxExtentFlat"));
! 1169: if (flat) {
! 1170: desc_extent_line = "RW %lld FLAT \"%s\" 0\n";
! 1171: } else {
! 1172: desc_extent_line = "RW %lld SPARSE \"%s\"\n";
! 1173: }
! 1174: if (flat && backing_file) {
! 1175: /* not supporting backing file for flat image */
! 1176: return -ENOTSUP;
! 1177: }
! 1178: if (backing_file) {
! 1179: char parent_filename[PATH_MAX];
! 1180: BlockDriverState *bs = bdrv_new("");
! 1181: ret = bdrv_open(bs, backing_file, 0, NULL);
! 1182: if (ret != 0) {
! 1183: bdrv_delete(bs);
! 1184: return ret;
! 1185: }
! 1186: if (strcmp(bs->drv->format_name, "vmdk")) {
! 1187: bdrv_delete(bs);
! 1188: return -EINVAL;
! 1189: }
! 1190: filesize = bdrv_getlength(bs);
! 1191: parent_cid = vmdk_read_cid(bs, 0);
! 1192: bdrv_delete(bs);
! 1193: relative_path(parent_filename, sizeof(parent_filename),
! 1194: filename, backing_file);
! 1195: snprintf(parent_desc_line, sizeof(parent_desc_line),
! 1196: "parentFileNameHint=\"%s\"", parent_filename);
! 1197: }
! 1198:
! 1199: /* Create extents */
! 1200: filesize = total_size;
! 1201: while (filesize > 0) {
! 1202: char desc_line[BUF_SIZE];
! 1203: char ext_filename[PATH_MAX];
! 1204: char desc_filename[PATH_MAX];
! 1205: int64_t size = filesize;
! 1206:
! 1207: if (split && size > split_size) {
! 1208: size = split_size;
! 1209: }
! 1210: if (split) {
! 1211: snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s",
! 1212: prefix, flat ? 'f' : 's', ++idx, postfix);
! 1213: } else if (flat) {
! 1214: snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s",
! 1215: prefix, postfix);
! 1216: } else {
! 1217: snprintf(desc_filename, sizeof(desc_filename), "%s%s",
! 1218: prefix, postfix);
! 1219: }
! 1220: snprintf(ext_filename, sizeof(ext_filename), "%s%s",
! 1221: path, desc_filename);
! 1222:
! 1223: if (vmdk_create_extent(ext_filename, size, flat)) {
! 1224: return -EINVAL;
! 1225: }
! 1226: filesize -= size;
! 1227:
! 1228: /* Format description line */
! 1229: snprintf(desc_line, sizeof(desc_line),
! 1230: desc_extent_line, size / 512, desc_filename);
! 1231: pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line);
! 1232: }
! 1233: /* generate descriptor file */
! 1234: snprintf(desc, sizeof(desc), desc_template,
! 1235: (unsigned int)time(NULL),
! 1236: parent_cid,
! 1237: fmt,
! 1238: parent_desc_line,
! 1239: ext_desc_lines,
! 1240: (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
! 1241: total_size / (int64_t)(63 * 16 * 512));
! 1242: if (split || flat) {
! 1243: fd = open(
! 1244: filename,
! 1245: O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
! 1246: 0644);
! 1247: } else {
! 1248: fd = open(
! 1249: filename,
! 1250: O_WRONLY | O_BINARY | O_LARGEFILE,
! 1251: 0644);
! 1252: }
! 1253: if (fd < 0) {
! 1254: return -errno;
! 1255: }
! 1256: /* the descriptor offset = 0x200 */
! 1257: if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
! 1258: ret = -errno;
! 1259: goto exit;
! 1260: }
1.1.1.3 root 1261: ret = qemu_write_full(fd, desc, strlen(desc));
1262: if (ret != strlen(desc)) {
1263: ret = -errno;
1264: goto exit;
1265: }
1266: ret = 0;
1267: exit:
1.1 root 1268: close(fd);
1.1.1.3 root 1269: return ret;
1.1 root 1270: }
1271:
1272: static void vmdk_close(BlockDriverState *bs)
1273: {
1.1.1.5 ! root 1274: vmdk_free_extents(bs);
1.1 root 1275: }
1276:
1.1.1.4 root 1277: static int vmdk_flush(BlockDriverState *bs)
1.1 root 1278: {
1.1.1.5 ! root 1279: int i, ret, err;
! 1280: BDRVVmdkState *s = bs->opaque;
! 1281:
! 1282: ret = bdrv_flush(bs->file);
! 1283: for (i = 0; i < s->num_extents; i++) {
! 1284: err = bdrv_flush(s->extents[i].file);
! 1285: if (err < 0) {
! 1286: ret = err;
! 1287: }
! 1288: }
! 1289: return ret;
1.1 root 1290: }
1291:
1.1.1.5 ! root 1292: static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
! 1293: {
! 1294: int i;
! 1295: int64_t ret = 0;
! 1296: int64_t r;
! 1297: BDRVVmdkState *s = bs->opaque;
! 1298:
! 1299: ret = bdrv_get_allocated_file_size(bs->file);
! 1300: if (ret < 0) {
! 1301: return ret;
! 1302: }
! 1303: for (i = 0; i < s->num_extents; i++) {
! 1304: if (s->extents[i].file == bs->file) {
! 1305: continue;
! 1306: }
! 1307: r = bdrv_get_allocated_file_size(s->extents[i].file);
! 1308: if (r < 0) {
! 1309: return r;
! 1310: }
! 1311: ret += r;
! 1312: }
! 1313: return ret;
! 1314: }
1.1 root 1315:
1316: static QEMUOptionParameter vmdk_create_options[] = {
1317: {
1318: .name = BLOCK_OPT_SIZE,
1319: .type = OPT_SIZE,
1320: .help = "Virtual disk size"
1321: },
1322: {
1323: .name = BLOCK_OPT_BACKING_FILE,
1324: .type = OPT_STRING,
1325: .help = "File name of a base image"
1326: },
1327: {
1328: .name = BLOCK_OPT_COMPAT6,
1329: .type = OPT_FLAG,
1330: .help = "VMDK version 6 image"
1331: },
1.1.1.5 ! root 1332: {
! 1333: .name = BLOCK_OPT_SUBFMT,
! 1334: .type = OPT_STRING,
! 1335: .help =
! 1336: "VMDK flat extent format, can be one of "
! 1337: "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} "
! 1338: },
1.1 root 1339: { NULL }
1340: };
1341:
1342: static BlockDriver bdrv_vmdk = {
1.1.1.5 ! root 1343: .format_name = "vmdk",
! 1344: .instance_size = sizeof(BDRVVmdkState),
! 1345: .bdrv_probe = vmdk_probe,
1.1.1.3 root 1346: .bdrv_open = vmdk_open,
1.1.1.5 ! root 1347: .bdrv_read = vmdk_read,
! 1348: .bdrv_write = vmdk_write,
! 1349: .bdrv_close = vmdk_close,
! 1350: .bdrv_create = vmdk_create,
! 1351: .bdrv_flush = vmdk_flush,
! 1352: .bdrv_is_allocated = vmdk_is_allocated,
! 1353: .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
1.1 root 1354:
1355: .create_options = vmdk_create_options,
1356: };
1357:
1358: static void bdrv_vmdk_init(void)
1359: {
1360: bdrv_register(&bdrv_vmdk);
1361: }
1362:
1363: block_init(bdrv_vmdk_init);
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.