156 |
156 |
#define CHECK_CID 1
|
157 |
157 |
|
158 |
158 |
#define SECTOR_SIZE 512
|
159 |
|
#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each
|
160 |
|
#define HEADER_SIZE 512 // first sector of 512 bytes
|
|
159 |
#define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */
|
|
160 |
#define BUF_SIZE 4096
|
|
161 |
#define HEADER_SIZE 512 /* first sector of 512 bytes */
|
161 |
162 |
|
162 |
163 |
static void vmdk_free_extents(BlockDriverState *bs)
|
163 |
164 |
{
|
... | ... | |
243 |
244 |
return 1;
|
244 |
245 |
}
|
245 |
246 |
|
246 |
|
static int vmdk_snapshot_create(const char *filename, const char *backing_file)
|
247 |
|
{
|
248 |
|
int snp_fd, p_fd;
|
249 |
|
int ret;
|
250 |
|
uint32_t p_cid;
|
251 |
|
char *p_name, *gd_buf, *rgd_buf;
|
252 |
|
const char *real_filename, *temp_str;
|
253 |
|
VMDK4Header header;
|
254 |
|
uint32_t gde_entries, gd_size;
|
255 |
|
int64_t gd_offset, rgd_offset, capacity, gt_size;
|
256 |
|
char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE];
|
257 |
|
static const char desc_template[] =
|
258 |
|
"# Disk DescriptorFile\n"
|
259 |
|
"version=1\n"
|
260 |
|
"CID=%x\n"
|
261 |
|
"parentCID=%x\n"
|
262 |
|
"createType=\"monolithicSparse\"\n"
|
263 |
|
"parentFileNameHint=\"%s\"\n"
|
264 |
|
"\n"
|
265 |
|
"# Extent description\n"
|
266 |
|
"RW %u SPARSE \"%s\"\n"
|
267 |
|
"\n"
|
268 |
|
"# The Disk Data Base \n"
|
269 |
|
"#DDB\n"
|
270 |
|
"\n";
|
271 |
|
|
272 |
|
snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
|
273 |
|
if (snp_fd < 0)
|
274 |
|
return -errno;
|
275 |
|
p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
|
276 |
|
if (p_fd < 0) {
|
277 |
|
close(snp_fd);
|
278 |
|
return -errno;
|
279 |
|
}
|
280 |
|
|
281 |
|
/* read the header */
|
282 |
|
if (lseek(p_fd, 0x0, SEEK_SET) == -1) {
|
283 |
|
ret = -errno;
|
284 |
|
goto fail;
|
285 |
|
}
|
286 |
|
if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) {
|
287 |
|
ret = -errno;
|
288 |
|
goto fail;
|
289 |
|
}
|
290 |
|
|
291 |
|
/* write the header */
|
292 |
|
if (lseek(snp_fd, 0x0, SEEK_SET) == -1) {
|
293 |
|
ret = -errno;
|
294 |
|
goto fail;
|
295 |
|
}
|
296 |
|
if (write(snp_fd, hdr, HEADER_SIZE) == -1) {
|
297 |
|
ret = -errno;
|
298 |
|
goto fail;
|
299 |
|
}
|
300 |
|
|
301 |
|
memset(&header, 0, sizeof(header));
|
302 |
|
memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
|
303 |
|
|
304 |
|
if (ftruncate(snp_fd, header.grain_offset << 9)) {
|
305 |
|
ret = -errno;
|
306 |
|
goto fail;
|
307 |
|
}
|
308 |
|
/* the descriptor offset = 0x200 */
|
309 |
|
if (lseek(p_fd, 0x200, SEEK_SET) == -1) {
|
310 |
|
ret = -errno;
|
311 |
|
goto fail;
|
312 |
|
}
|
313 |
|
if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) {
|
314 |
|
ret = -errno;
|
315 |
|
goto fail;
|
316 |
|
}
|
317 |
|
|
318 |
|
if ((p_name = strstr(p_desc,"CID")) != NULL) {
|
319 |
|
p_name += sizeof("CID");
|
320 |
|
sscanf(p_name,"%x",&p_cid);
|
321 |
|
}
|
322 |
|
|
323 |
|
real_filename = filename;
|
324 |
|
if ((temp_str = strrchr(real_filename, '\\')) != NULL)
|
325 |
|
real_filename = temp_str + 1;
|
326 |
|
if ((temp_str = strrchr(real_filename, '/')) != NULL)
|
327 |
|
real_filename = temp_str + 1;
|
328 |
|
if ((temp_str = strrchr(real_filename, ':')) != NULL)
|
329 |
|
real_filename = temp_str + 1;
|
330 |
|
|
331 |
|
snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file,
|
332 |
|
(uint32_t)header.capacity, real_filename);
|
333 |
|
|
334 |
|
/* write the descriptor */
|
335 |
|
if (lseek(snp_fd, 0x200, SEEK_SET) == -1) {
|
336 |
|
ret = -errno;
|
337 |
|
goto fail;
|
338 |
|
}
|
339 |
|
if (write(snp_fd, s_desc, strlen(s_desc)) == -1) {
|
340 |
|
ret = -errno;
|
341 |
|
goto fail;
|
342 |
|
}
|
343 |
|
|
344 |
|
gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table
|
345 |
|
rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table
|
346 |
|
capacity = header.capacity * SECTOR_SIZE; // Extent size
|
347 |
|
/*
|
348 |
|
* Each GDE span 32M disk, means:
|
349 |
|
* 512 GTE per GT, each GTE points to grain
|
350 |
|
*/
|
351 |
|
gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE;
|
352 |
|
if (!gt_size) {
|
353 |
|
ret = -EINVAL;
|
354 |
|
goto fail;
|
355 |
|
}
|
356 |
|
gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde
|
357 |
|
gd_size = gde_entries * sizeof(uint32_t);
|
358 |
|
|
359 |
|
/* write RGD */
|
360 |
|
rgd_buf = qemu_malloc(gd_size);
|
361 |
|
if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) {
|
362 |
|
ret = -errno;
|
363 |
|
goto fail_rgd;
|
364 |
|
}
|
365 |
|
if (read(p_fd, rgd_buf, gd_size) != gd_size) {
|
366 |
|
ret = -errno;
|
367 |
|
goto fail_rgd;
|
368 |
|
}
|
369 |
|
if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) {
|
370 |
|
ret = -errno;
|
371 |
|
goto fail_rgd;
|
372 |
|
}
|
373 |
|
if (write(snp_fd, rgd_buf, gd_size) == -1) {
|
374 |
|
ret = -errno;
|
375 |
|
goto fail_rgd;
|
376 |
|
}
|
377 |
|
|
378 |
|
/* write GD */
|
379 |
|
gd_buf = qemu_malloc(gd_size);
|
380 |
|
if (lseek(p_fd, gd_offset, SEEK_SET) == -1) {
|
381 |
|
ret = -errno;
|
382 |
|
goto fail_gd;
|
383 |
|
}
|
384 |
|
if (read(p_fd, gd_buf, gd_size) != gd_size) {
|
385 |
|
ret = -errno;
|
386 |
|
goto fail_gd;
|
387 |
|
}
|
388 |
|
if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) {
|
389 |
|
ret = -errno;
|
390 |
|
goto fail_gd;
|
391 |
|
}
|
392 |
|
if (write(snp_fd, gd_buf, gd_size) == -1) {
|
393 |
|
ret = -errno;
|
394 |
|
goto fail_gd;
|
395 |
|
}
|
396 |
|
ret = 0;
|
397 |
|
|
398 |
|
fail_gd:
|
399 |
|
qemu_free(gd_buf);
|
400 |
|
fail_rgd:
|
401 |
|
qemu_free(rgd_buf);
|
402 |
|
fail:
|
403 |
|
close(p_fd);
|
404 |
|
close(snp_fd);
|
405 |
|
return ret;
|
406 |
|
}
|
407 |
|
|
408 |
247 |
static int vmdk_parent_open(BlockDriverState *bs)
|
409 |
248 |
{
|
410 |
249 |
char *p_name;
|
... | ... | |
1058 |
897 |
return 0;
|
1059 |
898 |
}
|
1060 |
899 |
|
1061 |
|
static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|
900 |
|
|
901 |
static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat)
|
1062 |
902 |
{
|
1063 |
|
int fd, i;
|
|
903 |
int ret, i;
|
|
904 |
int fd = 0;
|
1064 |
905 |
VMDK4Header header;
|
1065 |
906 |
uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
|
1066 |
|
static const char desc_template[] =
|
1067 |
|
"# Disk DescriptorFile\n"
|
1068 |
|
"version=1\n"
|
1069 |
|
"CID=%x\n"
|
1070 |
|
"parentCID=ffffffff\n"
|
1071 |
|
"createType=\"monolithicSparse\"\n"
|
1072 |
|
"\n"
|
1073 |
|
"# Extent description\n"
|
1074 |
|
"RW %" PRId64 " SPARSE \"%s\"\n"
|
1075 |
|
"\n"
|
1076 |
|
"# The Disk Data Base \n"
|
1077 |
|
"#DDB\n"
|
1078 |
|
"\n"
|
1079 |
|
"ddb.virtualHWVersion = \"%d\"\n"
|
1080 |
|
"ddb.geometry.cylinders = \"%" PRId64 "\"\n"
|
1081 |
|
"ddb.geometry.heads = \"16\"\n"
|
1082 |
|
"ddb.geometry.sectors = \"63\"\n"
|
1083 |
|
"ddb.adapterType = \"ide\"\n";
|
1084 |
|
char desc[1024];
|
1085 |
|
const char *real_filename, *temp_str;
|
1086 |
|
int64_t total_size = 0;
|
1087 |
|
const char *backing_file = NULL;
|
1088 |
|
int flags = 0;
|
1089 |
|
int ret;
|
1090 |
907 |
|
1091 |
|
// Read out options
|
1092 |
|
while (options && options->name) {
|
1093 |
|
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
1094 |
|
total_size = options->value.n / 512;
|
1095 |
|
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
|
1096 |
|
backing_file = options->value.s;
|
1097 |
|
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
|
1098 |
|
flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0;
|
1099 |
|
}
|
1100 |
|
options++;
|
|
908 |
fd = open(
|
|
909 |
filename,
|
|
910 |
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
|
911 |
0644);
|
|
912 |
if (fd < 0) {
|
|
913 |
return -errno;
|
1101 |
914 |
}
|
1102 |
|
|
1103 |
|
/* XXX: add support for backing file */
|
1104 |
|
if (backing_file) {
|
1105 |
|
return vmdk_snapshot_create(filename, backing_file);
|
|
915 |
if (flat) {
|
|
916 |
ret = ftruncate(fd, filesize);
|
|
917 |
if (ret < 0) {
|
|
918 |
ret = -errno;
|
|
919 |
}
|
|
920 |
goto exit;
|
1106 |
921 |
}
|
1107 |
|
|
1108 |
|
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
1109 |
|
0644);
|
1110 |
|
if (fd < 0)
|
1111 |
|
return -errno;
|
1112 |
922 |
magic = cpu_to_be32(VMDK4_MAGIC);
|
1113 |
923 |
memset(&header, 0, sizeof(header));
|
1114 |
924 |
header.version = 1;
|
1115 |
925 |
header.flags = 3; /* ?? */
|
1116 |
|
header.capacity = total_size;
|
|
926 |
header.capacity = filesize / 512;
|
1117 |
927 |
header.granularity = 128;
|
1118 |
928 |
header.num_gtes_per_gte = 512;
|
1119 |
929 |
|
1120 |
|
grains = (total_size + header.granularity - 1) / header.granularity;
|
|
930 |
grains = (filesize / 512 + header.granularity - 1) / header.granularity;
|
1121 |
931 |
gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
|
1122 |
|
gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
|
|
932 |
gt_count =
|
|
933 |
(grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
|
1123 |
934 |
gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
|
1124 |
935 |
|
1125 |
936 |
header.desc_offset = 1;
|
... | ... | |
1130 |
941 |
((header.gd_offset + gd_size + (gt_size * gt_count) +
|
1131 |
942 |
header.granularity - 1) / header.granularity) *
|
1132 |
943 |
header.granularity;
|
1133 |
|
|
1134 |
944 |
/* swap endianness for all header fields */
|
1135 |
945 |
header.version = cpu_to_le32(header.version);
|
1136 |
946 |
header.flags = cpu_to_le32(header.flags);
|
... | ... | |
1188 |
998 |
}
|
1189 |
999 |
}
|
1190 |
1000 |
|
1191 |
|
/* compose the descriptor */
|
1192 |
|
real_filename = filename;
|
1193 |
|
if ((temp_str = strrchr(real_filename, '\\')) != NULL)
|
1194 |
|
real_filename = temp_str + 1;
|
1195 |
|
if ((temp_str = strrchr(real_filename, '/')) != NULL)
|
1196 |
|
real_filename = temp_str + 1;
|
1197 |
|
if ((temp_str = strrchr(real_filename, ':')) != NULL)
|
1198 |
|
real_filename = temp_str + 1;
|
1199 |
|
snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL),
|
1200 |
|
total_size, real_filename,
|
1201 |
|
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
1202 |
|
total_size / (int64_t)(63 * 16));
|
1203 |
|
|
1204 |
|
/* write the descriptor */
|
1205 |
|
lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
|
|
1001 |
ret = 0;
|
|
1002 |
exit:
|
|
1003 |
close(fd);
|
|
1004 |
return ret;
|
|
1005 |
}
|
|
1006 |
|
|
1007 |
static int filename_decompose(const char *filename, char *path, char *prefix,
|
|
1008 |
char *postfix, size_t buf_len)
|
|
1009 |
{
|
|
1010 |
const char *p, *q;
|
|
1011 |
|
|
1012 |
if (filename == NULL || !strlen(filename)) {
|
|
1013 |
fprintf(stderr, "Vmdk: no filename provided.\n");
|
|
1014 |
return -1;
|
|
1015 |
}
|
|
1016 |
p = strrchr(filename, '/');
|
|
1017 |
if (p == NULL) {
|
|
1018 |
p = strrchr(filename, '\\');
|
|
1019 |
}
|
|
1020 |
if (p == NULL) {
|
|
1021 |
p = strrchr(filename, ':');
|
|
1022 |
}
|
|
1023 |
if (p != NULL) {
|
|
1024 |
p++;
|
|
1025 |
if (p - filename >= buf_len) {
|
|
1026 |
return -1;
|
|
1027 |
}
|
|
1028 |
pstrcpy(path, p - filename + 1, filename);
|
|
1029 |
} else {
|
|
1030 |
p = filename;
|
|
1031 |
path[0] = '\0';
|
|
1032 |
}
|
|
1033 |
q = strrchr(p, '.');
|
|
1034 |
if (q == NULL) {
|
|
1035 |
pstrcpy(prefix, buf_len, p);
|
|
1036 |
postfix[0] = '\0';
|
|
1037 |
} else {
|
|
1038 |
if (q - p >= buf_len) {
|
|
1039 |
return -1;
|
|
1040 |
}
|
|
1041 |
pstrcpy(prefix, q - p + 1, p);
|
|
1042 |
pstrcpy(postfix, buf_len, q);
|
|
1043 |
}
|
|
1044 |
return 0;
|
|
1045 |
}
|
|
1046 |
|
|
1047 |
static int relative_path(char *dest, int dest_size,
|
|
1048 |
const char *base, const char *target)
|
|
1049 |
{
|
|
1050 |
int i = 0;
|
|
1051 |
int n = 0;
|
|
1052 |
const char *p, *q;
|
|
1053 |
#ifdef _WIN32
|
|
1054 |
const char *sep = "\\";
|
|
1055 |
#else
|
|
1056 |
const char *sep = "/";
|
|
1057 |
#endif
|
|
1058 |
|
|
1059 |
if (!(dest && base && target)) {
|
|
1060 |
return -1;
|
|
1061 |
}
|
|
1062 |
if (path_is_absolute(target)) {
|
|
1063 |
dest[dest_size - 1] = '\0';
|
|
1064 |
strncpy(dest, target, dest_size - 1);
|
|
1065 |
return 0;
|
|
1066 |
}
|
|
1067 |
while (base[i] == target[i]) {
|
|
1068 |
i++;
|
|
1069 |
}
|
|
1070 |
p = &base[i];
|
|
1071 |
q = &target[i];
|
|
1072 |
while (*p) {
|
|
1073 |
if (*p == *sep) {
|
|
1074 |
n++;
|
|
1075 |
}
|
|
1076 |
p++;
|
|
1077 |
}
|
|
1078 |
dest[0] = '\0';
|
|
1079 |
for (; n; n--) {
|
|
1080 |
pstrcat(dest, dest_size, "..");
|
|
1081 |
pstrcat(dest, dest_size, sep);
|
|
1082 |
}
|
|
1083 |
pstrcat(dest, dest_size, q);
|
|
1084 |
return 0;
|
|
1085 |
}
|
|
1086 |
|
|
1087 |
static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|
1088 |
{
|
|
1089 |
int fd, idx = 0;
|
|
1090 |
char desc[BUF_SIZE];
|
|
1091 |
int64_t total_size = 0, filesize;
|
|
1092 |
const char *backing_file = NULL;
|
|
1093 |
const char *fmt = NULL;
|
|
1094 |
int flags = 0;
|
|
1095 |
int ret = 0;
|
|
1096 |
bool flat, split;
|
|
1097 |
char ext_desc_lines[BUF_SIZE] = "";
|
|
1098 |
char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX];
|
|
1099 |
const int64_t split_size = 0x80000000; /* VMDK has constant split size */
|
|
1100 |
const char *desc_extent_line;
|
|
1101 |
char parent_desc_line[BUF_SIZE] = "";
|
|
1102 |
uint32_t parent_cid = 0xffffffff;
|
|
1103 |
const char desc_template[] =
|
|
1104 |
"# Disk DescriptorFile\n"
|
|
1105 |
"version=1\n"
|
|
1106 |
"CID=%x\n"
|
|
1107 |
"parentCID=%x\n"
|
|
1108 |
"createType=\"%s\"\n"
|
|
1109 |
"%s"
|
|
1110 |
"\n"
|
|
1111 |
"# Extent description\n"
|
|
1112 |
"%s"
|
|
1113 |
"\n"
|
|
1114 |
"# The Disk Data Base\n"
|
|
1115 |
"#DDB\n"
|
|
1116 |
"\n"
|
|
1117 |
"ddb.virtualHWVersion = \"%d\"\n"
|
|
1118 |
"ddb.geometry.cylinders = \"%" PRId64 "\"\n"
|
|
1119 |
"ddb.geometry.heads = \"16\"\n"
|
|
1120 |
"ddb.geometry.sectors = \"63\"\n"
|
|
1121 |
"ddb.adapterType = \"ide\"\n";
|
|
1122 |
|
|
1123 |
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
|
|
1124 |
return -EINVAL;
|
|
1125 |
}
|
|
1126 |
/* Read out options */
|
|
1127 |
while (options && options->name) {
|
|
1128 |
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
|
1129 |
total_size = options->value.n;
|
|
1130 |
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
|
|
1131 |
backing_file = options->value.s;
|
|
1132 |
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
|
|
1133 |
flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0;
|
|
1134 |
} else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
|
|
1135 |
fmt = options->value.s;
|
|
1136 |
}
|
|
1137 |
options++;
|
|
1138 |
}
|
|
1139 |
if (!fmt) {
|
|
1140 |
/* Default format to monolithicSparse */
|
|
1141 |
fmt = "monolithicSparse";
|
|
1142 |
} else if (strcmp(fmt, "monolithicFlat") &&
|
|
1143 |
strcmp(fmt, "monolithicSparse") &&
|
|
1144 |
strcmp(fmt, "twoGbMaxExtentSparse") &&
|
|
1145 |
strcmp(fmt, "twoGbMaxExtentFlat")) {
|
|
1146 |
fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt);
|
|
1147 |
return -EINVAL;
|
|
1148 |
}
|
|
1149 |
split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
|
|
1150 |
strcmp(fmt, "twoGbMaxExtentSparse"));
|
|
1151 |
flat = !(strcmp(fmt, "monolithicFlat") &&
|
|
1152 |
strcmp(fmt, "twoGbMaxExtentFlat"));
|
|
1153 |
if (flat) {
|
|
1154 |
desc_extent_line = "RW %lld FLAT \"%s\" 0\n";
|
|
1155 |
} else {
|
|
1156 |
desc_extent_line = "RW %lld SPARSE \"%s\"\n";
|
|
1157 |
}
|
|
1158 |
if (flat && backing_file) {
|
|
1159 |
/* not supporting backing file for flat image */
|
|
1160 |
return -ENOTSUP;
|
|
1161 |
}
|
|
1162 |
if (backing_file) {
|
|
1163 |
char parent_filename[PATH_MAX];
|
|
1164 |
BlockDriverState *bs = bdrv_new("");
|
|
1165 |
ret = bdrv_open(bs, backing_file, 0, NULL);
|
|
1166 |
if (ret != 0) {
|
|
1167 |
bdrv_delete(bs);
|
|
1168 |
return ret;
|
|
1169 |
}
|
|
1170 |
if (strcmp(bs->drv->format_name, "vmdk")) {
|
|
1171 |
bdrv_delete(bs);
|
|
1172 |
return -EINVAL;
|
|
1173 |
}
|
|
1174 |
filesize = bdrv_getlength(bs);
|
|
1175 |
parent_cid = vmdk_read_cid(bs, 0);
|
|
1176 |
bdrv_delete(bs);
|
|
1177 |
relative_path(parent_filename, sizeof(parent_filename),
|
|
1178 |
filename, backing_file);
|
|
1179 |
snprintf(parent_desc_line, sizeof(parent_desc_line),
|
|
1180 |
"parentFileNameHint=\"%s\"", parent_filename);
|
|
1181 |
}
|
|
1182 |
|
|
1183 |
/* Create extents */
|
|
1184 |
filesize = total_size;
|
|
1185 |
while (filesize > 0) {
|
|
1186 |
char desc_line[BUF_SIZE];
|
|
1187 |
char ext_filename[PATH_MAX];
|
|
1188 |
char desc_filename[PATH_MAX];
|
|
1189 |
int64_t size = filesize;
|
|
1190 |
|
|
1191 |
if (split && size > split_size) {
|
|
1192 |
size = split_size;
|
|
1193 |
}
|
|
1194 |
if (split) {
|
|
1195 |
snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s",
|
|
1196 |
prefix, flat ? 'f' : 's', ++idx, postfix);
|
|
1197 |
} else if (flat) {
|
|
1198 |
snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s",
|
|
1199 |
prefix, postfix);
|
|
1200 |
} else {
|
|
1201 |
snprintf(desc_filename, sizeof(desc_filename), "%s%s",
|
|
1202 |
prefix, postfix);
|
|
1203 |
}
|
|
1204 |
snprintf(ext_filename, sizeof(ext_filename), "%s%s",
|
|
1205 |
path, desc_filename);
|
|
1206 |
|
|
1207 |
if (vmdk_create_extent(ext_filename, size, flat)) {
|
|
1208 |
return -EINVAL;
|
|
1209 |
}
|
|
1210 |
filesize -= size;
|
|
1211 |
|
|
1212 |
/* Format description line */
|
|
1213 |
snprintf(desc_line, sizeof(desc_line),
|
|
1214 |
desc_extent_line, size / 512, desc_filename);
|
|
1215 |
pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line);
|
|
1216 |
}
|
|
1217 |
/* generate descriptor file */
|
|
1218 |
snprintf(desc, sizeof(desc), desc_template,
|
|
1219 |
(unsigned int)time(NULL),
|
|
1220 |
parent_cid,
|
|
1221 |
fmt,
|
|
1222 |
parent_desc_line,
|
|
1223 |
ext_desc_lines,
|
|
1224 |
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
|
1225 |
total_size / (int64_t)(63 * 16 * 512));
|
|
1226 |
if (split || flat) {
|
|
1227 |
fd = open(
|
|
1228 |
filename,
|
|
1229 |
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
|
1230 |
0644);
|
|
1231 |
} else {
|
|
1232 |
fd = open(
|
|
1233 |
filename,
|
|
1234 |
O_WRONLY | O_BINARY | O_LARGEFILE,
|
|
1235 |
0644);
|
|
1236 |
}
|
|
1237 |
if (fd < 0) {
|
|
1238 |
return -errno;
|
|
1239 |
}
|
|
1240 |
/* the descriptor offset = 0x200 */
|
|
1241 |
if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
|
|
1242 |
ret = -errno;
|
|
1243 |
goto exit;
|
|
1244 |
}
|
1206 |
1245 |
ret = qemu_write_full(fd, desc, strlen(desc));
|
1207 |
1246 |
if (ret != strlen(desc)) {
|
1208 |
1247 |
ret = -errno;
|
1209 |
1248 |
goto exit;
|
1210 |
1249 |
}
|
1211 |
|
|
1212 |
1250 |
ret = 0;
|
1213 |
1251 |
exit:
|
1214 |
1252 |
close(fd);
|
... | ... | |
1252 |
1290 |
.type = OPT_FLAG,
|
1253 |
1291 |
.help = "VMDK version 6 image"
|
1254 |
1292 |
},
|
|
1293 |
{
|
|
1294 |
.name = BLOCK_OPT_SUBFMT,
|
|
1295 |
.type = OPT_STRING,
|
|
1296 |
.help =
|
|
1297 |
"VMDK flat extent format, can be one of "
|
|
1298 |
"{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} "
|
|
1299 |
},
|
1255 |
1300 |
{ NULL }
|
1256 |
1301 |
};
|
1257 |
1302 |
|