Patchwork [05/26] FVD: add the 'qemu-img update' command

login
register
mail settings
Submitter Chunqiang Tang
Date Feb. 25, 2011, 10:37 p.m.
Message ID <1298673486-3573-5-git-send-email-ctang@us.ibm.com>
Download mbox | patch
Permalink /patch/84598/
State New
Headers show

Comments

Chunqiang Tang - Feb. 25, 2011, 10:37 p.m.
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 <ctang@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(-)

Patch

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);