Commit 57c7d9e57983d25ddf2ef995493366b9e15d32b4
1 parent
15d35bc5
block-vpc: Create images (Kevin Wolf)
Add an implementation to create VHD images. Signed-off-by: Kevin Wolf <kwolf@suse.de> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6459 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
169 additions
and
0 deletions
block-vpc.c
... | ... | @@ -37,6 +37,9 @@ enum vhd_type { |
37 | 37 | VHD_DIFFERENCING = 4, |
38 | 38 | }; |
39 | 39 | |
40 | +// Seconds since Jan 1, 2000 0:00:00 (UTC) | |
41 | +#define VHD_TIMESTAMP_BASE 946684800 | |
42 | + | |
40 | 43 | // always big-endian |
41 | 44 | struct vhd_footer { |
42 | 45 | char creator[8]; // "conectix" |
... | ... | @@ -127,6 +130,18 @@ typedef struct BDRVVPCState { |
127 | 130 | #endif |
128 | 131 | } BDRVVPCState; |
129 | 132 | |
133 | +static uint32_t vpc_checksum(uint8_t* buf, size_t size) | |
134 | +{ | |
135 | + uint32_t res = 0; | |
136 | + int i; | |
137 | + | |
138 | + for (i = 0; i < size; i++) | |
139 | + res += buf[i]; | |
140 | + | |
141 | + return ~res; | |
142 | +} | |
143 | + | |
144 | + | |
130 | 145 | static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) |
131 | 146 | { |
132 | 147 | if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) |
... | ... | @@ -141,6 +156,7 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags) |
141 | 156 | struct vhd_footer* footer; |
142 | 157 | struct vhd_dyndisk_header* dyndisk_header; |
143 | 158 | uint8_t buf[HEADER_SIZE]; |
159 | + uint32_t checksum; | |
144 | 160 | |
145 | 161 | ret = bdrv_file_open(&s->hd, filename, flags); |
146 | 162 | if (ret < 0) |
... | ... | @@ -153,6 +169,12 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags) |
153 | 169 | if (strncmp(footer->creator, "conectix", 8)) |
154 | 170 | goto fail; |
155 | 171 | |
172 | + checksum = be32_to_cpu(footer->checksum); | |
173 | + footer->checksum = 0; | |
174 | + if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) | |
175 | + fprintf(stderr, "block-vpc: The header checksum of '%s' is " | |
176 | + "incorrect.\n", filename); | |
177 | + | |
156 | 178 | // The visible size of a image in Virtual PC depends on the geometry |
157 | 179 | // rather than on the size stored in the footer (the size in the footer |
158 | 180 | // is too large usually) |
... | ... | @@ -407,6 +429,152 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num, |
407 | 429 | return 0; |
408 | 430 | } |
409 | 431 | |
432 | + | |
433 | +/* | |
434 | + * Calculates the number of cylinders, heads and sectors per cylinder | |
435 | + * based on a given number of sectors. This is the algorithm described | |
436 | + * in the VHD specification. | |
437 | + * | |
438 | + * Note that the geometry doesn't always exactly match total_sectors but | |
439 | + * may round it down. | |
440 | + */ | |
441 | +static void calculate_geometry(int64_t total_sectors, uint16_t* cyls, | |
442 | + uint8_t* heads, uint8_t* secs_per_cyl) | |
443 | +{ | |
444 | + uint32_t cyls_times_heads; | |
445 | + | |
446 | + if (total_sectors > 65535 * 16 * 255) | |
447 | + total_sectors = 65535 * 16 * 255; | |
448 | + | |
449 | + if (total_sectors > 65535 * 16 * 63) { | |
450 | + *secs_per_cyl = 255; | |
451 | + *heads = 16; | |
452 | + cyls_times_heads = total_sectors / *secs_per_cyl; | |
453 | + } else { | |
454 | + *secs_per_cyl = 17; | |
455 | + cyls_times_heads = total_sectors / *secs_per_cyl; | |
456 | + *heads = (cyls_times_heads + 1023) / 1024; | |
457 | + | |
458 | + if (*heads < 4) | |
459 | + *heads = 4; | |
460 | + | |
461 | + if (cyls_times_heads >= (*heads * 1024) || *heads > 16) { | |
462 | + *secs_per_cyl = 31; | |
463 | + *heads = 16; | |
464 | + cyls_times_heads = total_sectors / *secs_per_cyl; | |
465 | + } | |
466 | + | |
467 | + if (cyls_times_heads >= (*heads * 1024)) { | |
468 | + *secs_per_cyl = 63; | |
469 | + *heads = 16; | |
470 | + cyls_times_heads = total_sectors / *secs_per_cyl; | |
471 | + } | |
472 | + } | |
473 | + | |
474 | + // Note: Rounding up deviates from the Virtual PC behaviour | |
475 | + // However, we need this to avoid truncating images in qemu-img convert | |
476 | + *cyls = (cyls_times_heads + *heads - 1) / *heads; | |
477 | +} | |
478 | + | |
479 | +static int vpc_create(const char *filename, int64_t total_sectors, | |
480 | + const char *backing_file, int flags) | |
481 | +{ | |
482 | + uint8_t buf[1024]; | |
483 | + struct vhd_footer* footer = (struct vhd_footer*) buf; | |
484 | + struct vhd_dyndisk_header* dyndisk_header = | |
485 | + (struct vhd_dyndisk_header*) buf; | |
486 | + int fd, i; | |
487 | + uint16_t cyls; | |
488 | + uint8_t heads; | |
489 | + uint8_t secs_per_cyl; | |
490 | + size_t block_size, num_bat_entries; | |
491 | + | |
492 | + if (backing_file != NULL) | |
493 | + return -ENOTSUP; | |
494 | + | |
495 | + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); | |
496 | + if (fd < 0) | |
497 | + return -EIO; | |
498 | + | |
499 | + // Calculate matching total_size and geometry | |
500 | + calculate_geometry(total_sectors, &cyls, &heads, &secs_per_cyl); | |
501 | + total_sectors = (int64_t) cyls * heads * secs_per_cyl; | |
502 | + | |
503 | + // Prepare the Hard Disk Footer | |
504 | + memset(buf, 0, 1024); | |
505 | + | |
506 | + strncpy(footer->creator, "conectix", 8); | |
507 | + // TODO Check if "qemu" creator_app is ok for VPC | |
508 | + strncpy(footer->creator_app, "qemu", 4); | |
509 | + strncpy(footer->creator_os, "Wi2k", 4); | |
510 | + | |
511 | + footer->features = be32_to_cpu(0x02); | |
512 | + footer->version = be32_to_cpu(0x00010000); | |
513 | + footer->data_offset = be64_to_cpu(HEADER_SIZE); | |
514 | + footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE); | |
515 | + | |
516 | + // Version of Virtual PC 2007 | |
517 | + footer->major = be16_to_cpu(0x0005); | |
518 | + footer->minor =be16_to_cpu(0x0003); | |
519 | + | |
520 | + footer->orig_size = be64_to_cpu(total_sectors * 512); | |
521 | + footer->size = be64_to_cpu(total_sectors * 512); | |
522 | + | |
523 | + footer->cyls = be16_to_cpu(cyls); | |
524 | + footer->heads = heads; | |
525 | + footer->secs_per_cyl = secs_per_cyl; | |
526 | + | |
527 | + footer->type = be32_to_cpu(VHD_DYNAMIC); | |
528 | + | |
529 | + // TODO uuid is missing | |
530 | + | |
531 | + footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE)); | |
532 | + | |
533 | + // Write the footer (twice: at the beginning and at the end) | |
534 | + block_size = 0x200000; | |
535 | + num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); | |
536 | + | |
537 | + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) | |
538 | + return -EIO; | |
539 | + | |
540 | + if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) | |
541 | + return -EIO; | |
542 | + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) | |
543 | + return -EIO; | |
544 | + | |
545 | + // Write the initial BAT | |
546 | + if (lseek(fd, 3 * 512, SEEK_SET) < 0) | |
547 | + return -EIO; | |
548 | + | |
549 | + memset(buf, 0xFF, 512); | |
550 | + for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) | |
551 | + if (write(fd, buf, 512) != 512) | |
552 | + return -EIO; | |
553 | + | |
554 | + | |
555 | + // Prepare the Dynamic Disk Header | |
556 | + memset(buf, 0, 1024); | |
557 | + | |
558 | + strncpy(dyndisk_header->magic, "cxsparse", 8); | |
559 | + | |
560 | + dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFF); | |
561 | + dyndisk_header->table_offset = be64_to_cpu(3 * 512); | |
562 | + dyndisk_header->version = be32_to_cpu(0x00010000); | |
563 | + dyndisk_header->block_size = be32_to_cpu(block_size); | |
564 | + dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries); | |
565 | + | |
566 | + dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); | |
567 | + | |
568 | + // Write the header | |
569 | + if (lseek(fd, 512, SEEK_SET) < 0) | |
570 | + return -EIO; | |
571 | + if (write(fd, buf, 1024) != 1024) | |
572 | + return -EIO; | |
573 | + | |
574 | + close(fd); | |
575 | + return 0; | |
576 | +} | |
577 | + | |
410 | 578 | static void vpc_close(BlockDriverState *bs) |
411 | 579 | { |
412 | 580 | BDRVVPCState *s = bs->opaque; |
... | ... | @@ -425,4 +593,5 @@ BlockDriver bdrv_vpc = { |
425 | 593 | vpc_read, |
426 | 594 | vpc_write, |
427 | 595 | vpc_close, |
596 | + vpc_create, | |
428 | 597 | }; | ... | ... |