This patch is part of the Fast Virtual Disk (FVD) proposal. See http://wiki.qemu.org/Features/FVD.
This patch adds the 'update' command to qemu-img. It is a general interface that allows various image format specific manipulations. For example, 'qemu-img rebase' and 'qemu-img resize' can be considered as two special cases of update. Signed-off-by: Chunqiang Tang <ct...@us.ibm.com> --- block_int.h | 3 + qemu-img-cmds.hx | 6 +++ qemu-img.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++------- qemu-option.c | 79 ++++++++++++++++++++++++++++++++++ qemu-option.h | 4 ++ 5 files changed, 201 insertions(+), 16 deletions(-) diff --git a/block_int.h b/block_int.h index 545ad11..8f6b6d0 100644 --- a/block_int.h +++ b/block_int.h @@ -98,6 +98,7 @@ struct BlockDriver { int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, const char *snapshot_name); int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); + int (*bdrv_update)(BlockDriverState *bs, QEMUOptionParameter *options); int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size); @@ -122,6 +123,8 @@ struct BlockDriver { /* List of options for creating images, terminated by name == NULL */ QEMUOptionParameter *create_options; + /* List of options for updating images, terminated by name == NULL */ + QEMUOptionParameter *update_options; /* * Returns 0 for completed check, -errno for internal errors. diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 6c7176f..a7ed395 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -39,6 +39,12 @@ STEXI @item info [-f @var{fmt}] @var{filename} ETEXI +DEF("update", img_update, + "update [-f fmt] [-o options] filename") +STEXI +@item update [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] +ETEXI + DEF("snapshot", img_snapshot, "snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename") STEXI diff --git a/qemu-img.c b/qemu-img.c index 7e3cc4c..215e7b9 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -179,10 +179,11 @@ static int read_password(char *buf, int buf_size) } #endif -static int print_block_option_help(const char *filename, const char *fmt) +static int print_block_option_help(const char *filename, const char *fmt, + bool create_options) { BlockDriver *drv, *proto_drv; - QEMUOptionParameter *create_options = NULL; + QEMUOptionParameter *options = NULL; /* Find driver and parse its options */ drv = bdrv_find_format(fmt); @@ -197,12 +198,15 @@ static int print_block_option_help(const char *filename, const char *fmt) return 1; } - create_options = append_option_parameters(create_options, - drv->create_options); - create_options = append_option_parameters(create_options, - proto_drv->create_options); - print_option_help(create_options); - free_option_parameters(create_options); + if (create_options) { + options = append_option_parameters(options, drv->create_options); + options = append_option_parameters(options, proto_drv->create_options); + } else { + options = append_option_parameters(options, drv->update_options); + options = append_option_parameters(options, proto_drv->update_options); + } + print_option_help(options); + free_option_parameters(options); return 0; } @@ -337,7 +341,7 @@ static int img_create(int argc, char **argv) } if (options && !strcmp(options, "?")) { - ret = print_block_option_help(filename, fmt); + ret = print_block_option_help(filename, fmt, true /*create*/); goto out; } @@ -631,7 +635,7 @@ static int img_convert(int argc, char **argv) out_filename = argv[argc - 1]; if (options && !strcmp(options, "?")) { - ret = print_block_option_help(out_filename, out_fmt); + ret = print_block_option_help(out_filename, out_fmt, true /*create*/); goto out; } @@ -869,7 +873,7 @@ static int img_convert(int argc, char **argv) assume that sectors which are unallocated in the input image are present in both the output's and input's base images (no need to copy them). */ - if (out_baseimg) { + if (out_baseimg || bs[bs_i]->backing_file[0]==0) { if (!bdrv_is_allocated(bs[bs_i], sector_num - bs_offset, n, &n1)) { sector_num += n1; @@ -1040,11 +1044,6 @@ static int img_info(int argc, char **argv) if (bdrv_is_encrypted(bs)) { printf("encrypted: yes\n"); } - if (bdrv_get_info(bs, &bdi) >= 0) { - if (bdi.cluster_size != 0) { - printf("cluster_size: %d\n", bdi.cluster_size); - } - } bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); if (backing_filename[0] != '\0') { path_combine(backing_filename2, sizeof(backing_filename2), @@ -1053,11 +1052,105 @@ static int img_info(int argc, char **argv) backing_filename, backing_filename2); } + if (bdrv_get_info(bs, &bdi) >= 0) { + if (bdi.cluster_size != 0) + printf("cluster_size: %d\n", bdi.cluster_size); + } dump_snapshots(bs); bdrv_delete(bs); return 0; } +static int img_update(int argc, char **argv) +{ + int c, ret = 0; + const char *filename, *fmt = NULL; + BlockDriverState *bs; + char *options = NULL; + QEMUOptionParameter *param = NULL, *option_template = NULL; + BlockDriver *drv, *proto_drv; + char fmt_name[128]; + + for(;;) { + c = getopt(argc, argv, "f:o:h"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + break; + case 'f': + fmt = optarg; + break; + case 'o': + options = optarg; + break; + } + } + if (optind >= argc) + help(); + filename = argv[optind++]; + if (!options) { + error_report("No options were given\n"); + return -EINVAL; + } + + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING + | BDRV_O_RDWR); + if (!bs) { + ret = -EIO; + goto out; + } + + bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); + + if (!strcmp(options, "?")) { + return print_block_option_help(filename, fmt_name, false /*update*/); + } + + if (!bs->drv->bdrv_update) { + error_report("the 'update' command is not supported for the '%s' " + "image format.", fmt_name); + goto out; + } + + /* Find driver and parse its options */ + drv = bdrv_find_format(fmt_name); + if (!drv) { + error_report("Unknown file format '%s'", fmt_name); + ret = -EINVAL; + goto out; + } + + proto_drv = bdrv_find_protocol(filename); + if (!proto_drv) { + error_report("Unknown protocol '%s'", filename); + ret = -EINVAL; + goto out; + } + + option_template = append_option_parameters(option_template, + drv->update_options); + option_template = append_option_parameters(option_template, + proto_drv->update_options); + + if (!(param = parse_specified_parameters(options, option_template))) { + error_report("Invalid options for file format '%s'.", fmt_name); + ret = -EINVAL; + goto out; + } + + ret = bs->drv->bdrv_update(bs, param); + +out: + free_option_parameters(option_template); + free_option_parameters(param); + if(bs) { + bdrv_delete(bs); + } + return ret; +} + #define SNAPSHOT_LIST 1 #define SNAPSHOT_CREATE 2 #define SNAPSHOT_APPLY 3 diff --git a/qemu-option.c b/qemu-option.c index 65db542..28b19b5 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -289,6 +289,10 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name, return -1; break; + case OPT_NUMBER: + list->value.n = atoi (value); + break; + default: fprintf(stderr, "Bug: Option '%s' has an unknown type\n", name); return -1; @@ -391,6 +395,22 @@ QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest, return dest; } +QEMUOptionParameter *append_one_option_parameter(QEMUOptionParameter *dest, + QEMUOptionParameter *param) +{ + QEMUOptionParameter *target; + if ((target = get_option_parameter(dest, param->name))) { + *target = *param; + } else { + size_t n = count_option_parameters(dest); + dest = qemu_realloc(dest, (n + 2) * sizeof(QEMUOptionParameter)); + dest[n] = *param; + dest[n + 1].name = NULL; + } + + return dest; +} + /* * Parses a parameter string (param) into an option list (dest). * @@ -461,6 +481,65 @@ fail: } /* + * Parses a parameter string (param) into an option list (dest). + * + * list is the template option list. If list is NULL, this function fails. + * Only options explicitly specified in param are returned in dest. + */ +QEMUOptionParameter *parse_specified_parameters(const char *param, + QEMUOptionParameter *list) +{ + QEMUOptionParameter *dest = NULL; + char name[256]; + char value[256]; + char *param_delim, *value_delim; + char next_delim; + QEMUOptionParameter *opt; + + if (list == NULL) { + return NULL; + } + + while (*param) { + /* Find parameter name and value in the string. */ + param_delim = strchr(param, ','); + value_delim = strchr(param, '='); + + if (value_delim && (value_delim < param_delim || !param_delim)) { + next_delim = '='; + } else { + next_delim = ','; + value_delim = NULL; + } + + param = get_opt_name(name, sizeof(name), param, next_delim); + if (value_delim) { + param = get_opt_value(value, sizeof(value), param + 1); + } + if (*param != '\0') { + param++; + } + + /* Set the parameter in the template. */ + if (set_option_parameter(list, name, value_delim ? value : NULL)) { + goto fail; + } + + /* Copy from template to dest. */ + opt = get_option_parameter(list, name); + dest = append_one_option_parameter(dest, opt); + } + + return dest; + +fail: + if (dest) { + free_option_parameters(dest); + } + return NULL; +} + +/* * Prints all options of a list that have a value to stdout */ void print_option_parameters(QEMUOptionParameter *list) diff --git a/qemu-option.h b/qemu-option.h index b515813..81ca734 100644 --- a/qemu-option.h +++ b/qemu-option.h @@ -72,8 +72,12 @@ int set_option_parameter_int(QEMUOptionParameter *list, const char *name, uint64_t value); QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest, QEMUOptionParameter *list); +QEMUOptionParameter *append_one_option_parameter(QEMUOptionParameter *dest, + QEMUOptionParameter *param); QEMUOptionParameter *parse_option_parameters(const char *param, QEMUOptionParameter *list, QEMUOptionParameter *dest); +QEMUOptionParameter *parse_specified_parameters(const char *param, + QEMUOptionParameter *list); void free_option_parameters(QEMUOptionParameter *list); void print_option_parameters(QEMUOptionParameter *list); void print_option_help(QEMUOptionParameter *list); -- 1.7.0.4