Am 01.02.2012 00:04, schrieb Charles Arnold: > The Virtual Hard Disk Image Format Specification allows for three > types of hard disk formats, Fixed, Dynamic, and Differencing. Qemu > currently only supports Dynamic disks. This patch adds support for > the Fixed Disk format. > > Usage: > Example 1: qemu-img create -f vpc -o type=fixed <filename> [size] > Example 2: qemu-img convert -O vpc -o type=fixed <input filename> <output > filename> > > While it is also allowed to specify '-o type=dynamic', the default disk type > remains Dynamic and is what is used when the type is left unspecified. > > Signed-off-by: Charles Arnold <carn...@suse.com>
Reviewed-by: Andreas Färber <afaer...@suse.de> Thanks, looking good now AFAICS. Andreas > > diff --git a/block/vpc.c b/block/vpc.c > index 89a5ee2..04db372 100644 > --- a/block/vpc.c > +++ b/block/vpc.c > @@ -160,14 +160,25 @@ static int vpc_open(BlockDriverState *bs, int flags) > struct vhd_dyndisk_header* dyndisk_header; > uint8_t buf[HEADER_SIZE]; > uint32_t checksum; > + int disk_type = VHD_DYNAMIC; > int err = -1; > > if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) > goto fail; > > footer = (struct vhd_footer*) s->footer_buf; > - if (strncmp(footer->creator, "conectix", 8)) > - goto fail; > + if (strncmp(footer->creator, "conectix", 8)) { > + int64_t offset = bdrv_getlength(bs->file); > + /* If a fixed disk, the footer is found only at the end of the file > */ > + if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, > HEADER_SIZE) > + != HEADER_SIZE) { > > + goto fail; > > + } > > + if (strncmp(footer->creator, "conectix", 8)) { > > + goto fail; > > + } > > + disk_type = VHD_FIXED; > > + } > > > > checksum = be32_to_cpu(footer->checksum); > > footer->checksum = 0; > > @@ -186,6 +197,14 @@ static int vpc_open(BlockDriverState *bs, int flags) > > goto fail; > > } > > > > + /* The footer is all that is needed for fixed disks */ > > + if (disk_type == VHD_FIXED) { > > + /* The fixed disk format doesn't use footer->data_offset but it > > + should be initialized */ > > + footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL); > > + return 0; > > + } > > + > > if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, > HEADER_SIZE) > != HEADER_SIZE) > > goto fail; > > @@ -533,10 +552,10 @@ static int calculate_geometry(int64_t total_sectors, > uint16_t* cyls, > return 0; > > } > > > > -static int vpc_create(const char *filename, QEMUOptionParameter *options) > > +static int vpc_create_dynamic_disk(const char *filename, int64_t total_size) > > { > > uint8_t buf[1024]; > > - struct vhd_footer* footer = (struct vhd_footer*) buf; > > + struct vhd_footer* footer = (struct vhd_footer *) buf; > > struct vhd_dyndisk_header* dyndisk_header = > > (struct vhd_dyndisk_header*) buf; > > int fd, i; > > @@ -544,13 +563,9 @@ static int vpc_create(const char *filename, > QEMUOptionParameter *options) > uint8_t heads = 0; > > uint8_t secs_per_cyl = 0; > > size_t block_size, num_bat_entries; > > - int64_t total_sectors = 0; > > + int64_t total_sectors = total_size / BDRV_SECTOR_SIZE; > > int ret = -EIO; > > > > - // Read out options > > - total_sectors = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n / > > - BDRV_SECTOR_SIZE; > > - > > // Create the file > > fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); > > if (fd < 0) > > @@ -657,6 +672,101 @@ static int vpc_create(const char *filename, > QEMUOptionParameter *options) > return ret; > > } > > > > +static int vpc_create_fixed_disk(const char *filename, int64_t total_size) > > +{ > > + uint8_t buf[1024]; > > + struct vhd_footer* footer = (struct vhd_footer *) buf; > > + int fd; > > + uint16_t cyls = 0; > > + uint8_t heads = 0; > > + uint8_t secs_per_cyl = 0; > > + int64_t total_sectors = total_size / BDRV_SECTOR_SIZE; > > + int ret = -EIO; > > + > > + /* Create the file */ > > + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); > > + if (fd < 0) { > > + return -EIO; > > + } > > + > > + /* Calculate matching total_size and geometry. */ > > + if (calculate_geometry(total_sectors, &cyls, &heads, &secs_per_cyl)) { > > + ret = -EFBIG; > > + goto fail; > > + } > > + total_sectors = (int64_t) cyls * heads * secs_per_cyl; > > + > > + /* Prepare the Hard Disk Footer */ > > + memset(buf, 0, 1024); > > + > > + memcpy(footer->creator, "conectix", 8); > > + /* TODO Check if "qemu" creator_app is ok for VPC */ > > + memcpy(footer->creator_app, "qemu", 4); > > + memcpy(footer->creator_os, "Wi2k", 4); > > + > > + footer->features = be32_to_cpu(0x02); > > + footer->version = be32_to_cpu(0x00010000); > > + footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL); > > + footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE); > > + > > + /* Version of Virtual PC 2007 */ > > + footer->major = be16_to_cpu(0x0005); > > + footer->minor = be16_to_cpu(0x0003); > > + footer->orig_size = be64_to_cpu(total_size); > > + footer->size = be64_to_cpu(total_size); > > + footer->cyls = be16_to_cpu(cyls); > > + footer->heads = heads; > > + footer->secs_per_cyl = secs_per_cyl; > > + > > + footer->type = be32_to_cpu(VHD_FIXED); > > + > > + /* TODO uuid is missing */ > > + > > + footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE)); > > + > > + total_size += 512; > > + if (ftruncate(fd, total_size) != 0) { > > + ret = -errno; > > + goto fail; > > + } > > + if (lseek(fd, -512, SEEK_END) < 0) { > > + goto fail; > > + } > > + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { > + goto fail; > + } > + > + ret = 0; > + > + fail: > + close(fd); > + return ret; > +} > + > +static int vpc_create(const char *filename, QEMUOptionParameter *options) > +{ > + int64_t total_size = 0; > + QEMUOptionParameter *disk_type_param; > + int ret = -EIO; > + > + /* Read out options */ > + total_size = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n; > + > + disk_type_param = get_option_parameter(options, BLOCK_OPT_TYPE); > + if (disk_type_param && disk_type_param->value.s) { > + if (!strcmp(disk_type_param->value.s, "dynamic")) { > + ret = vpc_create_dynamic_disk(filename, total_size); > + } else if (!strcmp(disk_type_param->value.s, "fixed")) { > + ret = vpc_create_fixed_disk(filename, total_size); > + } else { > + ret = -EINVAL; > + } > + } else { > + ret = vpc_create_dynamic_disk(filename, total_size); > + } > + return ret; > +} > + > static void vpc_close(BlockDriverState *bs) > { > BDRVVPCState *s = bs->opaque; > @@ -675,6 +785,13 @@ static QEMUOptionParameter vpc_create_options[] = { > .type = OPT_SIZE, > .help = "Virtual disk size" > }, > + { > + .name = BLOCK_OPT_TYPE, > + .type = OPT_STRING, > + .help = > + "Type of virtual hard disk format. Supported formats are " > + "{dynamic (default) | fixed} " > + }, > { NULL } > }; > > diff --git a/block_int.h b/block_int.h > index 311bd2a..6d6cc96 100644 > --- a/block_int.h > +++ b/block_int.h > @@ -50,6 +50,7 @@ > #define BLOCK_OPT_TABLE_SIZE "table_size" > #define BLOCK_OPT_PREALLOC "preallocation" > #define BLOCK_OPT_SUBFMT "subformat" > +#define BLOCK_OPT_TYPE "type" > > typedef struct BdrvTrackedRequest BdrvTrackedRequest; > -- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg