Commit 57c7d9e57983d25ddf2ef995493366b9e15d32b4

Authored by aliguori
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 };
... ...