aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qemu/patches/0011-VMDK-create-different-subformats.patch
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qemu/patches/0011-VMDK-create-different-subformats.patch')
-rw-r--r--tools/qemu/patches/0011-VMDK-create-different-subformats.patch595
1 files changed, 595 insertions, 0 deletions
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 <famcool@gmail.com>
+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 <famcool@gmail.com>
+Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+---
+ 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);