From 9e2e48ff31cea669cfc9c1476f49b815ea9ac1b1 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Wed, 2 Oct 2019 10:28:16 -0400 Subject: tools: qemu: Add patches to support adapter_type and monolithicFlat Its way more trouble to update this to a newer version of qemu than it is to backport the two additional features we need. Signed-off-by: Brett Mastbergen --- .../0011-VMDK-create-different-subformats.patch | 595 +++++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 tools/qemu/patches/0011-VMDK-create-different-subformats.patch (limited to 'tools/qemu/patches/0011-VMDK-create-different-subformats.patch') diff --git a/tools/qemu/patches/0011-VMDK-create-different-subformats.patch b/tools/qemu/patches/0011-VMDK-create-different-subformats.patch new file mode 100644 index 0000000000..7f02772d03 --- /dev/null +++ b/tools/qemu/patches/0011-VMDK-create-different-subformats.patch @@ -0,0 +1,595 @@ +From 0d0f2ba577bd05491b5954751787f8b969ca1ec3 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Tue, 19 Jul 2011 08:45:23 +0800 +Subject: [PATCH 11/12] VMDK: create different subformats + +Add create option 'format', with enums: + monolithicSparse + monolithicFlat + twoGbMaxExtentSparse + twoGbMaxExtentFlat +Each creates a subformat image file. The default is monolithicSparse. + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Kevin Wolf +--- + block/vmdk.c | 503 ++++++++++++++++++++++++++++++++--------------------------- + block_int.h | 1 + + 2 files changed, 275 insertions(+), 229 deletions(-) + +--- a/block/vmdk.c ++++ b/block/vmdk.c +@@ -156,8 +156,9 @@ static int vmdk_probe(const uint8_t *buf + #define CHECK_CID 1 + + #define SECTOR_SIZE 512 +-#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each +-#define HEADER_SIZE 512 // first sector of 512 bytes ++#define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */ ++#define BUF_SIZE 4096 ++#define HEADER_SIZE 512 /* first sector of 512 bytes */ + + static void vmdk_free_extents(BlockDriverState *bs) + { +@@ -243,168 +244,6 @@ static int vmdk_is_cid_valid(BlockDriver + return 1; + } + +-static int vmdk_snapshot_create(const char *filename, const char *backing_file) +-{ +- int snp_fd, p_fd; +- int ret; +- uint32_t p_cid; +- char *p_name, *gd_buf, *rgd_buf; +- const char *real_filename, *temp_str; +- VMDK4Header header; +- uint32_t gde_entries, gd_size; +- int64_t gd_offset, rgd_offset, capacity, gt_size; +- char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE]; +- static const char desc_template[] = +- "# Disk DescriptorFile\n" +- "version=1\n" +- "CID=%x\n" +- "parentCID=%x\n" +- "createType=\"monolithicSparse\"\n" +- "parentFileNameHint=\"%s\"\n" +- "\n" +- "# Extent description\n" +- "RW %u SPARSE \"%s\"\n" +- "\n" +- "# The Disk Data Base \n" +- "#DDB\n" +- "\n"; +- +- snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); +- if (snp_fd < 0) +- return -errno; +- p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); +- if (p_fd < 0) { +- close(snp_fd); +- return -errno; +- } +- +- /* read the header */ +- if (lseek(p_fd, 0x0, SEEK_SET) == -1) { +- ret = -errno; +- goto fail; +- } +- if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) { +- ret = -errno; +- goto fail; +- } +- +- /* write the header */ +- if (lseek(snp_fd, 0x0, SEEK_SET) == -1) { +- ret = -errno; +- goto fail; +- } +- if (write(snp_fd, hdr, HEADER_SIZE) == -1) { +- ret = -errno; +- goto fail; +- } +- +- memset(&header, 0, sizeof(header)); +- memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC +- +- if (ftruncate(snp_fd, header.grain_offset << 9)) { +- ret = -errno; +- goto fail; +- } +- /* the descriptor offset = 0x200 */ +- if (lseek(p_fd, 0x200, SEEK_SET) == -1) { +- ret = -errno; +- goto fail; +- } +- if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) { +- ret = -errno; +- goto fail; +- } +- +- if ((p_name = strstr(p_desc,"CID")) != NULL) { +- p_name += sizeof("CID"); +- sscanf(p_name,"%x",&p_cid); +- } +- +- real_filename = filename; +- if ((temp_str = strrchr(real_filename, '\\')) != NULL) +- real_filename = temp_str + 1; +- if ((temp_str = strrchr(real_filename, '/')) != NULL) +- real_filename = temp_str + 1; +- if ((temp_str = strrchr(real_filename, ':')) != NULL) +- real_filename = temp_str + 1; +- +- snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file, +- (uint32_t)header.capacity, real_filename); +- +- /* write the descriptor */ +- if (lseek(snp_fd, 0x200, SEEK_SET) == -1) { +- ret = -errno; +- goto fail; +- } +- if (write(snp_fd, s_desc, strlen(s_desc)) == -1) { +- ret = -errno; +- goto fail; +- } +- +- gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table +- rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table +- capacity = header.capacity * SECTOR_SIZE; // Extent size +- /* +- * Each GDE span 32M disk, means: +- * 512 GTE per GT, each GTE points to grain +- */ +- gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; +- if (!gt_size) { +- ret = -EINVAL; +- goto fail; +- } +- gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde +- gd_size = gde_entries * sizeof(uint32_t); +- +- /* write RGD */ +- rgd_buf = qemu_malloc(gd_size); +- if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) { +- ret = -errno; +- goto fail_rgd; +- } +- if (read(p_fd, rgd_buf, gd_size) != gd_size) { +- ret = -errno; +- goto fail_rgd; +- } +- if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) { +- ret = -errno; +- goto fail_rgd; +- } +- if (write(snp_fd, rgd_buf, gd_size) == -1) { +- ret = -errno; +- goto fail_rgd; +- } +- +- /* write GD */ +- gd_buf = qemu_malloc(gd_size); +- if (lseek(p_fd, gd_offset, SEEK_SET) == -1) { +- ret = -errno; +- goto fail_gd; +- } +- if (read(p_fd, gd_buf, gd_size) != gd_size) { +- ret = -errno; +- goto fail_gd; +- } +- if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) { +- ret = -errno; +- goto fail_gd; +- } +- if (write(snp_fd, gd_buf, gd_size) == -1) { +- ret = -errno; +- goto fail_gd; +- } +- ret = 0; +- +-fail_gd: +- qemu_free(gd_buf); +-fail_rgd: +- qemu_free(rgd_buf); +-fail: +- close(p_fd); +- close(snp_fd); +- return ret; +-} +- + static int vmdk_parent_open(BlockDriverState *bs) + { + char *p_name; +@@ -1058,68 +897,40 @@ static int vmdk_write(BlockDriverState * + return 0; + } + +-static int vmdk_create(const char *filename, QEMUOptionParameter *options) ++ ++static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat) + { +- int fd, i; ++ int ret, i; ++ int fd = 0; + VMDK4Header header; + uint32_t tmp, magic, grains, gd_size, gt_size, gt_count; +- static const char desc_template[] = +- "# Disk DescriptorFile\n" +- "version=1\n" +- "CID=%x\n" +- "parentCID=ffffffff\n" +- "createType=\"monolithicSparse\"\n" +- "\n" +- "# Extent description\n" +- "RW %" PRId64 " SPARSE \"%s\"\n" +- "\n" +- "# The Disk Data Base \n" +- "#DDB\n" +- "\n" +- "ddb.virtualHWVersion = \"%d\"\n" +- "ddb.geometry.cylinders = \"%" PRId64 "\"\n" +- "ddb.geometry.heads = \"16\"\n" +- "ddb.geometry.sectors = \"63\"\n" +- "ddb.adapterType = \"ide\"\n"; +- char desc[1024]; +- const char *real_filename, *temp_str; +- int64_t total_size = 0; +- const char *backing_file = NULL; +- int flags = 0; +- int ret; + +- // Read out options +- while (options && options->name) { +- if (!strcmp(options->name, BLOCK_OPT_SIZE)) { +- total_size = options->value.n / 512; +- } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { +- backing_file = options->value.s; +- } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { +- flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0; +- } +- options++; ++ fd = open( ++ filename, ++ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, ++ 0644); ++ if (fd < 0) { ++ return -errno; + } +- +- /* XXX: add support for backing file */ +- if (backing_file) { +- return vmdk_snapshot_create(filename, backing_file); ++ if (flat) { ++ ret = ftruncate(fd, filesize); ++ if (ret < 0) { ++ ret = -errno; ++ } ++ goto exit; + } +- +- fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, +- 0644); +- if (fd < 0) +- return -errno; + magic = cpu_to_be32(VMDK4_MAGIC); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.flags = 3; /* ?? */ +- header.capacity = total_size; ++ header.capacity = filesize / 512; + header.granularity = 128; + header.num_gtes_per_gte = 512; + +- grains = (total_size + header.granularity - 1) / header.granularity; ++ grains = (filesize / 512 + header.granularity - 1) / header.granularity; + gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; +- gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; ++ gt_count = ++ (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; + gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; + + header.desc_offset = 1; +@@ -1130,7 +941,6 @@ static int vmdk_create(const char *filen + ((header.gd_offset + gd_size + (gt_size * gt_count) + + header.granularity - 1) / header.granularity) * + header.granularity; +- + /* swap endianness for all header fields */ + header.version = cpu_to_le32(header.version); + header.flags = cpu_to_le32(header.flags); +@@ -1188,27 +998,255 @@ static int vmdk_create(const char *filen + } + } + +- /* compose the descriptor */ +- real_filename = filename; +- if ((temp_str = strrchr(real_filename, '\\')) != NULL) +- real_filename = temp_str + 1; +- if ((temp_str = strrchr(real_filename, '/')) != NULL) +- real_filename = temp_str + 1; +- if ((temp_str = strrchr(real_filename, ':')) != NULL) +- real_filename = temp_str + 1; +- snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL), +- total_size, real_filename, +- (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), +- total_size / (int64_t)(63 * 16)); ++ ret = 0; ++ exit: ++ close(fd); ++ return ret; ++} + +- /* write the descriptor */ +- lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); ++static int filename_decompose(const char *filename, char *path, char *prefix, ++ char *postfix, size_t buf_len) ++{ ++ const char *p, *q; ++ ++ if (filename == NULL || !strlen(filename)) { ++ fprintf(stderr, "Vmdk: no filename provided.\n"); ++ return -1; ++ } ++ p = strrchr(filename, '/'); ++ if (p == NULL) { ++ p = strrchr(filename, '\\'); ++ } ++ if (p == NULL) { ++ p = strrchr(filename, ':'); ++ } ++ if (p != NULL) { ++ p++; ++ if (p - filename >= buf_len) { ++ return -1; ++ } ++ pstrcpy(path, p - filename + 1, filename); ++ } else { ++ p = filename; ++ path[0] = '\0'; ++ } ++ q = strrchr(p, '.'); ++ if (q == NULL) { ++ pstrcpy(prefix, buf_len, p); ++ postfix[0] = '\0'; ++ } else { ++ if (q - p >= buf_len) { ++ return -1; ++ } ++ pstrcpy(prefix, q - p + 1, p); ++ pstrcpy(postfix, buf_len, q); ++ } ++ return 0; ++} ++ ++static int relative_path(char *dest, int dest_size, ++ const char *base, const char *target) ++{ ++ int i = 0; ++ int n = 0; ++ const char *p, *q; ++#ifdef _WIN32 ++ const char *sep = "\\"; ++#else ++ const char *sep = "/"; ++#endif ++ ++ if (!(dest && base && target)) { ++ return -1; ++ } ++ if (path_is_absolute(target)) { ++ dest[dest_size - 1] = '\0'; ++ strncpy(dest, target, dest_size - 1); ++ return 0; ++ } ++ while (base[i] == target[i]) { ++ i++; ++ } ++ p = &base[i]; ++ q = &target[i]; ++ while (*p) { ++ if (*p == *sep) { ++ n++; ++ } ++ p++; ++ } ++ dest[0] = '\0'; ++ for (; n; n--) { ++ pstrcat(dest, dest_size, ".."); ++ pstrcat(dest, dest_size, sep); ++ } ++ pstrcat(dest, dest_size, q); ++ return 0; ++} ++ ++static int vmdk_create(const char *filename, QEMUOptionParameter *options) ++{ ++ int fd, idx = 0; ++ char desc[BUF_SIZE]; ++ int64_t total_size = 0, filesize; ++ const char *backing_file = NULL; ++ const char *fmt = NULL; ++ int flags = 0; ++ int ret = 0; ++ bool flat, split; ++ char ext_desc_lines[BUF_SIZE] = ""; ++ char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX]; ++ const int64_t split_size = 0x80000000; /* VMDK has constant split size */ ++ const char *desc_extent_line; ++ char parent_desc_line[BUF_SIZE] = ""; ++ uint32_t parent_cid = 0xffffffff; ++ const char desc_template[] = ++ "# Disk DescriptorFile\n" ++ "version=1\n" ++ "CID=%x\n" ++ "parentCID=%x\n" ++ "createType=\"%s\"\n" ++ "%s" ++ "\n" ++ "# Extent description\n" ++ "%s" ++ "\n" ++ "# The Disk Data Base\n" ++ "#DDB\n" ++ "\n" ++ "ddb.virtualHWVersion = \"%d\"\n" ++ "ddb.geometry.cylinders = \"%" PRId64 "\"\n" ++ "ddb.geometry.heads = \"16\"\n" ++ "ddb.geometry.sectors = \"63\"\n" ++ "ddb.adapterType = \"ide\"\n"; ++ ++ if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { ++ return -EINVAL; ++ } ++ /* Read out options */ ++ while (options && options->name) { ++ if (!strcmp(options->name, BLOCK_OPT_SIZE)) { ++ total_size = options->value.n; ++ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { ++ backing_file = options->value.s; ++ } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { ++ flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0; ++ } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) { ++ fmt = options->value.s; ++ } ++ options++; ++ } ++ if (!fmt) { ++ /* Default format to monolithicSparse */ ++ fmt = "monolithicSparse"; ++ } else if (strcmp(fmt, "monolithicFlat") && ++ strcmp(fmt, "monolithicSparse") && ++ strcmp(fmt, "twoGbMaxExtentSparse") && ++ strcmp(fmt, "twoGbMaxExtentFlat")) { ++ fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt); ++ return -EINVAL; ++ } ++ split = !(strcmp(fmt, "twoGbMaxExtentFlat") && ++ strcmp(fmt, "twoGbMaxExtentSparse")); ++ flat = !(strcmp(fmt, "monolithicFlat") && ++ strcmp(fmt, "twoGbMaxExtentFlat")); ++ if (flat) { ++ desc_extent_line = "RW %lld FLAT \"%s\" 0\n"; ++ } else { ++ desc_extent_line = "RW %lld SPARSE \"%s\"\n"; ++ } ++ if (flat && backing_file) { ++ /* not supporting backing file for flat image */ ++ return -ENOTSUP; ++ } ++ if (backing_file) { ++ char parent_filename[PATH_MAX]; ++ BlockDriverState *bs = bdrv_new(""); ++ ret = bdrv_open(bs, backing_file, 0, NULL); ++ if (ret != 0) { ++ bdrv_delete(bs); ++ return ret; ++ } ++ if (strcmp(bs->drv->format_name, "vmdk")) { ++ bdrv_delete(bs); ++ return -EINVAL; ++ } ++ filesize = bdrv_getlength(bs); ++ parent_cid = vmdk_read_cid(bs, 0); ++ bdrv_delete(bs); ++ relative_path(parent_filename, sizeof(parent_filename), ++ filename, backing_file); ++ snprintf(parent_desc_line, sizeof(parent_desc_line), ++ "parentFileNameHint=\"%s\"", parent_filename); ++ } ++ ++ /* Create extents */ ++ filesize = total_size; ++ while (filesize > 0) { ++ char desc_line[BUF_SIZE]; ++ char ext_filename[PATH_MAX]; ++ char desc_filename[PATH_MAX]; ++ int64_t size = filesize; ++ ++ if (split && size > split_size) { ++ size = split_size; ++ } ++ if (split) { ++ snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s", ++ prefix, flat ? 'f' : 's', ++idx, postfix); ++ } else if (flat) { ++ snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s", ++ prefix, postfix); ++ } else { ++ snprintf(desc_filename, sizeof(desc_filename), "%s%s", ++ prefix, postfix); ++ } ++ snprintf(ext_filename, sizeof(ext_filename), "%s%s", ++ path, desc_filename); ++ ++ if (vmdk_create_extent(ext_filename, size, flat)) { ++ return -EINVAL; ++ } ++ filesize -= size; ++ ++ /* Format description line */ ++ snprintf(desc_line, sizeof(desc_line), ++ desc_extent_line, size / 512, desc_filename); ++ pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line); ++ } ++ /* generate descriptor file */ ++ snprintf(desc, sizeof(desc), desc_template, ++ (unsigned int)time(NULL), ++ parent_cid, ++ fmt, ++ parent_desc_line, ++ ext_desc_lines, ++ (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), ++ total_size / (int64_t)(63 * 16 * 512)); ++ if (split || flat) { ++ fd = open( ++ filename, ++ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, ++ 0644); ++ } else { ++ fd = open( ++ filename, ++ O_WRONLY | O_BINARY | O_LARGEFILE, ++ 0644); ++ } ++ if (fd < 0) { ++ return -errno; ++ } ++ /* the descriptor offset = 0x200 */ ++ if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) { ++ ret = -errno; ++ goto exit; ++ } + ret = qemu_write_full(fd, desc, strlen(desc)); + if (ret != strlen(desc)) { + ret = -errno; + goto exit; + } +- + ret = 0; + exit: + close(fd); +@@ -1252,6 +1290,13 @@ static QEMUOptionParameter vmdk_create_o + .type = OPT_FLAG, + .help = "VMDK version 6 image" + }, ++ { ++ .name = BLOCK_OPT_SUBFMT, ++ .type = OPT_STRING, ++ .help = ++ "VMDK flat extent format, can be one of " ++ "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} " ++ }, + { NULL } + }; + +--- a/block_int.h ++++ b/block_int.h +@@ -39,6 +39,7 @@ + #define BLOCK_OPT_CLUSTER_SIZE "cluster_size" + #define BLOCK_OPT_TABLE_SIZE "table_size" + #define BLOCK_OPT_PREALLOC "preallocation" ++#define BLOCK_OPT_SUBFMT "subformat" + + typedef struct AIOPool { + void (*cancel)(BlockDriverAIOCB *acb); -- cgit v1.2.3