mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2024-12-24 15:43:25 +00:00
9e2e48ff31
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 <bmastbergen@untangle.com>
596 lines
18 KiB
Diff
596 lines
18 KiB
Diff
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);
|