A flag is added, as there are basically two extent type: SPARSE and FLAT, this flat can be used in future subformats too. Create vmdk mono flat image with `qemu-img create -o flat`.
Signed-off-by: Fam Zheng <famc...@gmail.com> --- block/vmdk.c | 297 ++++++++++++++++++++++++++++++++++++---------------------- block_int.h | 1 + 2 files changed, 185 insertions(+), 113 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index 85e868e..43b47e2 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -835,7 +835,7 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, ret = get_cluster_offset(bs, extent, NULL, sector_num << 9, 0, &offset); if (ret) { return 0; - } + } index_in_cluster = sector_num % extent->cluster_sectors; n = extent->cluster_sectors - index_in_cluster; } @@ -957,32 +957,16 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, static int vmdk_create(const char *filename, QEMUOptionParameter *options) { - int fd, i; + int fd; + int64_t i; 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; + bool flat = false; int ret; // Read out options @@ -993,114 +977,196 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) 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_FLAT)) { + flat = options->value.n; } options++; } - /* XXX: add support for backing file */ - if (backing_file) { - return vmdk_snapshot_create(filename, backing_file); - } - - 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 = cpu_to_le32(1); - header.flags = cpu_to_le32(3); /* ?? */ - header.capacity = cpu_to_le64(total_size); - header.granularity = cpu_to_le64(128); - header.num_gtes_per_gte = cpu_to_le32(512); - - grains = (total_size + 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; - gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; - - header.desc_offset = 1; - header.desc_size = 20; - header.rgd_offset = header.desc_offset + header.desc_size; - header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count); - header.grain_offset = - ((header.gd_offset + gd_size + (gt_size * gt_count) + - header.granularity - 1) / header.granularity) * - header.granularity; - - header.desc_offset = cpu_to_le64(header.desc_offset); - header.desc_size = cpu_to_le64(header.desc_size); - header.rgd_offset = cpu_to_le64(header.rgd_offset); - header.gd_offset = cpu_to_le64(header.gd_offset); - header.grain_offset = cpu_to_le64(header.grain_offset); - - header.check_bytes[0] = 0xa; - header.check_bytes[1] = 0x20; - header.check_bytes[2] = 0xd; - header.check_bytes[3] = 0xa; - - /* write all the data */ - ret = qemu_write_full(fd, &magic, sizeof(magic)); - if (ret != sizeof(magic)) { - ret = -errno; - goto exit; - } - ret = qemu_write_full(fd, &header, sizeof(header)); - if (ret != sizeof(header)) { - ret = -errno; - goto exit; - } - - ret = ftruncate(fd, header.grain_offset << 9); - if (ret < 0) { - ret = -errno; - goto exit; - } + if (flat) { + const char desc_template[] = + "# Disk DescriptorFile\n" + "version=1\n" + "CID=%x\n" + "parentCID=ffffffff\n" + "createType=\"monolithicFlat\"\n" + "\n" + "# Extent description\n" + "RW %" PRId64 " FLAT \"%s\" 0\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 ext_filename[1024]; + strncpy(ext_filename, filename, 1024); + ext_filename[1023] = '\0'; + if (backing_file) { + /* not supporting backing file for flat image */ + return -1; + } + if (!strcmp(&ext_filename[strlen(ext_filename) - 5], ".vmdk")) + strcpy(&ext_filename[strlen(ext_filename) - 5], "-flat.vmdk"); + else + strcat(ext_filename, "-flat.vmdk"); + /* create extent first */ + fd = open( + ext_filename, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) + return -errno; + ret = ftruncate(fd, total_size * 512); + if (ret) goto exit; + close(fd); + + /* generate descriptor file */ + snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL), + total_size, ext_filename, + (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), + total_size / (int64_t)(63 * 16)); + fd = open( + filename, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) + return -errno; + ret = qemu_write_full(fd, desc, strlen(desc)); + if (ret != strlen(desc)) { + ret = -errno; + goto exit; + } + ret = 0; + } else { + 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"; + /* XXX: add support for backing file */ + if (backing_file) { + return vmdk_snapshot_create(filename, backing_file); + } - /* write grain directory */ - lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); - for (i = 0, tmp = header.rgd_offset + gd_size; - i < gt_count; i++, tmp += gt_size) { - ret = qemu_write_full(fd, &tmp, sizeof(tmp)); - if (ret != sizeof(tmp)) { + 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 = cpu_to_le32(1); + header.flags = cpu_to_le32(3); /* ?? */ + header.capacity = cpu_to_le64(total_size); + header.granularity = cpu_to_le64(128); + header.num_gtes_per_gte = cpu_to_le32(512); + + grains = (total_size + 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; + gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; + + header.desc_offset = 1; + header.desc_size = 20; + header.rgd_offset = header.desc_offset + header.desc_size; + header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count); + header.grain_offset = + ((header.gd_offset + gd_size + (gt_size * gt_count) + + header.granularity - 1) / header.granularity) * + header.granularity; + + header.desc_offset = cpu_to_le64(header.desc_offset); + header.desc_size = cpu_to_le64(header.desc_size); + header.rgd_offset = cpu_to_le64(header.rgd_offset); + header.gd_offset = cpu_to_le64(header.gd_offset); + header.grain_offset = cpu_to_le64(header.grain_offset); + + header.check_bytes[0] = 0xa; + header.check_bytes[1] = 0x20; + header.check_bytes[2] = 0xd; + header.check_bytes[3] = 0xa; + + /* write all the data */ + ret = qemu_write_full(fd, &magic, sizeof(magic)); + if (ret != sizeof(magic)) { + ret = -errno; + goto exit; + } + ret = qemu_write_full(fd, &header, sizeof(header)); + if (ret != sizeof(header)) { ret = -errno; goto exit; } - } - /* write backup grain directory */ - lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); - for (i = 0, tmp = header.gd_offset + gd_size; - i < gt_count; i++, tmp += gt_size) { - ret = qemu_write_full(fd, &tmp, sizeof(tmp)); - if (ret != sizeof(tmp)) { + ret = ftruncate(fd, header.grain_offset << 9); + if (ret < 0) { ret = -errno; goto exit; } - } - /* 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)); + /* write grain directory */ + lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); + for (i = 0, tmp = header.rgd_offset + gd_size; + i < gt_count; i++, tmp += gt_size) { + ret = qemu_write_full(fd, &tmp, sizeof(tmp)); + if (ret != sizeof(tmp)) { + ret = -errno; + goto exit; + } + } - /* write the descriptor */ - lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); - ret = qemu_write_full(fd, desc, strlen(desc)); - if (ret != strlen(desc)) { - ret = -errno; - goto exit; - } + /* write backup grain directory */ + lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); + for (i = 0, tmp = header.gd_offset + gd_size; + i < gt_count; i++, tmp += gt_size) { + ret = qemu_write_full(fd, &tmp, sizeof(tmp)); + if (ret != sizeof(tmp)) { + ret = -errno; + goto exit; + } + } - ret = 0; + /* 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)); + + /* write the descriptor */ + lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); + ret = qemu_write_full(fd, desc, strlen(desc)); + if (ret != strlen(desc)) { + ret = -errno; + goto exit; + } + ret = 0; + } exit: close(fd); return ret; @@ -1148,6 +1214,11 @@ static QEMUOptionParameter vmdk_create_options[] = { .type = OPT_FLAG, .help = "VMDK version 6 image" }, + { + .name = BLOCK_OPT_FLAT, + .type = OPT_FLAG, + .help = "VMDK flat extent image" + }, { NULL } }; diff --git a/block_int.h b/block_int.h index fa91337..dd8f8cb 100644 --- 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_FLAT "flat" typedef struct AIOPool { void (*cancel)(BlockDriverAIOCB *acb);