From a7a6a2bffcc4410da29427a87808272cca91e335 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Thu, 11 May 2017 18:03:37 +0300 Subject: [PATCH 01/10] qcow2: remove extra local_error variable Commit d7086422b1c1e75e320519cfe26176db6ec97a37 added a local_err variable global to the qcow2_amend_options() function, so there's no need to have this other one. Signed-off-by: Alberto Garcia Message-id: 20170511150337.21470-1-berto@igalia.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- block/qcow2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index a8d61f0981..b3ba5daa93 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3222,7 +3222,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (s->refcount_bits != refcount_bits) { int refcount_order = ctz32(refcount_bits); - Error *local_error = NULL; if (new_version < 3 && refcount_bits != 16) { error_report("Different refcount widths than 16 bits require " @@ -3234,9 +3233,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER; ret = qcow2_change_refcount_order(bs, refcount_order, &qcow2_amend_helper_cb, - &helper_cb_info, &local_error); + &helper_cb_info, &local_err); if (ret < 0) { - error_report_err(local_error); + error_report_err(local_err); return ret; } } From caa31bf28c0c3e2930540ad6a95935f92551b7eb Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Mon, 15 May 2017 18:35:51 +0800 Subject: [PATCH 02/10] qemu-img: Fix documentation of convert It got lost in commit a8d16f9ca "qemu-img: Update documentation for -U". Reported-by: Max Reitz Signed-off-by: Fam Zheng Message-id: 20170515103551.31313-1-famz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- qemu-img-cmds.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index e5bc28fc3c..31141f97f4 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -40,9 +40,9 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") STEXI -@item convert [--object @var{objectdef}] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("dd", img_dd, From 83d4bf943e09cbcb011e255c872724e95fe4856e Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 15 May 2017 17:47:09 +0100 Subject: [PATCH 03/10] qemu-img: add support for --object with 'dd' command The qemu-img dd command added --image-opts support, but missed the corresponding --object support. This prevented passing secrets (eg auth passwords) needed by certain disk images. Reviewed-by: Fam Zheng Reviewed-by: Max Reitz Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrange Message-id: 20170515164712.6643-2-berrange@redhat.com Signed-off-by: Max Reitz --- qemu-img.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/qemu-img.c b/qemu-img.c index b506839ef0..181f49956b 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4158,6 +4158,7 @@ static int img_dd(int argc, char **argv) }; const struct option long_options[] = { { "help", no_argument, 0, 'h'}, + { "object", required_argument, 0, OPTION_OBJECT}, { "image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, { "force-share", no_argument, 0, 'U'}, { 0, 0, 0, 0 } @@ -4186,6 +4187,15 @@ static int img_dd(int argc, char **argv) case 'U': force_share = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + ret = -1; + goto out; + } + } break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -4230,6 +4240,14 @@ static int img_dd(int argc, char **argv) ret = -1; goto out; } + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + ret = -1; + goto out; + } + blk1 = img_open(image_opts, in.filename, fmt, 0, false, false, force_share); From ea204ddac7340bfda60cb0b388dbc3ffd77e8be0 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 15 May 2017 17:47:10 +0100 Subject: [PATCH 04/10] qemu-img: fix --image-opts usage with dd command The --image-opts flag can only be used to affect the parsing of the source image. The target image has to be specified in the traditional style regardless, since it needs to be passed to the bdrv_create() API which does not support the new style opts. Reviewed-by: Fam Zheng Reviewed-by: Max Reitz Signed-off-by: Daniel P. Berrange Message-id: 20170515164712.6643-3-berrange@redhat.com Signed-off-by: Max Reitz --- qemu-img.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 181f49956b..4dc1d56cee 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4316,8 +4316,13 @@ static int img_dd(int argc, char **argv) goto out; } - blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR, - false, false, false); + /* TODO, we can't honour --image-opts for the target, + * since it needs to be given in a format compatible + * with the bdrv_create() call above which does not + * support image-opts style. + */ + blk2 = img_open_file(out.filename, out_fmt, BDRV_O_RDWR, + false, false, false); if (!blk2) { ret = -1; From 305b4c60f200ee8e6267ac75f3f5b5d09fda1079 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 15 May 2017 17:47:11 +0100 Subject: [PATCH 05/10] qemu-img: introduce --target-image-opts for 'convert' command The '--image-opts' flag indicates whether the source filename includes options. The target filename has to remain in the plain filename format though, since it needs to be passed to bdrv_create(). When using --skip-create though, it would be possible to use image-opts syntax. This adds --target-image-opts to indicate that the target filename includes options. Currently this mandates use of the --skip-create flag too. Signed-off-by: Daniel P. Berrange Message-id: 20170515164712.6643-4-berrange@redhat.com Reviewed-by: Max Reitz Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- qemu-img-cmds.hx | 4 +-- qemu-img.c | 86 ++++++++++++++++++++++++++++++++---------------- qemu-img.texi | 12 +++++-- 3 files changed, 70 insertions(+), 32 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 31141f97f4..a39fcdba71 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -40,9 +40,9 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") STEXI -@item convert [--object @var{objectdef}] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("dd", img_dd, diff --git a/qemu-img.c b/qemu-img.c index 4dc1d56cee..e0e3d31455 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -60,6 +60,7 @@ enum { OPTION_PATTERN = 260, OPTION_FLUSH_INTERVAL = 261, OPTION_NO_DRAIN = 262, + OPTION_TARGET_IMAGE_OPTS = 263, }; typedef enum OutputFormat { @@ -1913,10 +1914,10 @@ static int convert_do_copy(ImgConvertState *s) static int img_convert(int argc, char **argv) { int c, bs_i, flags, src_flags = 0; - const char *fmt = NULL, *out_fmt = "raw", *cache = "unsafe", + const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe", *src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL, *out_filename, *out_baseimg_param, *snapshot_name = NULL; - BlockDriver *drv, *proto_drv; + BlockDriver *drv = NULL, *proto_drv = NULL; BlockDriverInfo bdi; BlockDriverState *out_bs; QemuOpts *opts = NULL, *sn_opts = NULL; @@ -1924,7 +1925,7 @@ static int img_convert(int argc, char **argv) char *options = NULL; Error *local_err = NULL; bool writethrough, src_writethrough, quiet = false, image_opts = false, - skip_create = false, progress = false; + skip_create = false, progress = false, tgt_image_opts = false; int64_t ret = -EINVAL; bool force_share = false; @@ -1942,6 +1943,7 @@ static int img_convert(int argc, char **argv) {"object", required_argument, 0, OPTION_OBJECT}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"force-share", no_argument, 0, 'U'}, + {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, ":hf:O:B:ce6o:s:l:S:pt:T:qnm:WU", @@ -2062,9 +2064,16 @@ static int img_convert(int argc, char **argv) case OPTION_IMAGE_OPTS: image_opts = true; break; + case OPTION_TARGET_IMAGE_OPTS: + tgt_image_opts = true; + break; } } + if (!out_fmt && !tgt_image_opts) { + out_fmt = "raw"; + } + if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, NULL, NULL)) { @@ -2076,12 +2085,22 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } + if (tgt_image_opts && !skip_create) { + error_report("--target-image-opts requires use of -n flag"); + goto fail_getopt; + } + s.src_num = argc - optind - 1; out_filename = s.src_num >= 1 ? argv[argc - 1] : NULL; if (options && has_help_option(options)) { - ret = print_block_option_help(out_filename, out_fmt); - goto fail_getopt; + if (out_fmt) { + ret = print_block_option_help(out_filename, out_fmt); + goto fail_getopt; + } else { + error_report("Option help requires a format be specified"); + goto fail_getopt; + } } if (s.src_num < 1) { @@ -2146,22 +2165,22 @@ static int img_convert(int argc, char **argv) goto out; } - /* Find driver and parse its options */ - drv = bdrv_find_format(out_fmt); - if (!drv) { - error_report("Unknown file format '%s'", out_fmt); - ret = -1; - goto out; - } - - proto_drv = bdrv_find_protocol(out_filename, true, &local_err); - if (!proto_drv) { - error_report_err(local_err); - ret = -1; - goto out; - } - if (!skip_create) { + /* Find driver and parse its options */ + drv = bdrv_find_format(out_fmt); + if (!drv) { + error_report("Unknown file format '%s'", out_fmt); + ret = -1; + goto out; + } + + proto_drv = bdrv_find_protocol(out_filename, true, &local_err); + if (!proto_drv) { + error_report_err(local_err); + ret = -1; + goto out; + } + if (!drv->create_opts) { error_report("Format driver '%s' does not support image creation", drv->format_name); @@ -2218,7 +2237,7 @@ static int img_convert(int argc, char **argv) const char *preallocation = qemu_opt_get(opts, BLOCK_OPT_PREALLOC); - if (!drv->bdrv_co_pwritev_compressed) { + if (drv && !drv->bdrv_co_pwritev_compressed) { error_report("Compression not supported for this file format"); ret = -1; goto out; @@ -2258,19 +2277,30 @@ static int img_convert(int argc, char **argv) goto out; } - /* XXX we should allow --image-opts to trigger use of - * img_open() here, but then we have trouble with - * the bdrv_create() call which takes different params. - * Not critical right now, so fix can wait... - */ - s.target = img_open_file(out_filename, out_fmt, flags, writethrough, quiet, - false); + if (skip_create) { + s.target = img_open(tgt_image_opts, out_filename, out_fmt, + flags, writethrough, quiet, false); + } else { + /* TODO ultimately we should allow --target-image-opts + * to be used even when -n is not given. + * That has to wait for bdrv_create to be improved + * to allow filenames in option syntax + */ + s.target = img_open_file(out_filename, out_fmt, flags, + writethrough, quiet, false); + } if (!s.target) { ret = -1; goto out; } out_bs = blk_bs(s.target); + if (s.compressed && !out_bs->drv->bdrv_co_pwritev_compressed) { + error_report("Compression not supported for this file format"); + ret = -1; + goto out; + } + /* increase bufsectors from the default 4096 (2M) if opt_transfer * or discard_alignment of the out_bs is greater. Limit to 32768 (16MB) * as maximum. */ diff --git a/qemu-img.texi b/qemu-img.texi index 50a2364e80..5b925ecf41 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -45,9 +45,17 @@ keys. @item --image-opts -Indicates that the @var{filename} parameter is to be interpreted as a +Indicates that the source @var{filename} parameter is to be interpreted as a full option string, not a plain filename. This parameter is mutually -exclusive with the @var{-f} and @var{-F} parameters. +exclusive with the @var{-f} parameter. + +@item --target-image-opts + +Indicates that the @var{output_filename} parameter(s) are to be interpreted as +a full option string, not a plain filename. This parameter is mutually +exclusive with the @var{-O} parameters. It is currently required to also use +the @var{-n} parameter to skip image creation. This restriction may be relaxed +in a future release. @item fmt is the disk image format. It is guessed automatically in most cases. See below From 29cf933635a50a4f1c51b022b323089997471e38 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 15 May 2017 17:47:12 +0100 Subject: [PATCH 06/10] qemu-img: copy *key-secret opts when opening newly created files The qemu-img dd/convert commands will create an image file and then try to open it. Historically it has been possible to open new files without passing any options. With encrypted files though, the *key-secret options are mandatory, so we need to provide those options when opening the newly created file. Signed-off-by: Daniel P. Berrange Message-id: 20170515164712.6643-5-berrange@redhat.com Reviewed-by: Max Reitz Signed-off-by: Max Reitz --- qemu-img.c | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index e0e3d31455..0bf941ba56 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -314,14 +314,17 @@ static BlockBackend *img_open_opts(const char *optstr, } static BlockBackend *img_open_file(const char *filename, + QDict *options, const char *fmt, int flags, bool writethrough, bool quiet, bool force_share) { BlockBackend *blk; Error *local_err = NULL; - QDict *options = qdict_new(); + if (!options) { + options = qdict_new(); + } if (fmt) { qdict_put_str(options, "driver", fmt); } @@ -344,6 +347,35 @@ static BlockBackend *img_open_file(const char *filename, } +static int img_add_key_secrets(void *opaque, + const char *name, const char *value, + Error **errp) +{ + QDict *options = opaque; + + if (g_str_has_suffix(name, "key-secret")) { + qdict_put(options, name, qstring_from_str(value)); + } + + return 0; +} + +static BlockBackend *img_open_new_file(const char *filename, + QemuOpts *create_opts, + const char *fmt, int flags, + bool writethrough, bool quiet, + bool force_share) +{ + QDict *options = NULL; + + options = qdict_new(); + qemu_opt_foreach(create_opts, img_add_key_secrets, options, &error_abort); + + return img_open_file(filename, options, fmt, flags, writethrough, quiet, + force_share); +} + + static BlockBackend *img_open(bool image_opts, const char *filename, const char *fmt, int flags, bool writethrough, @@ -364,7 +396,7 @@ static BlockBackend *img_open(bool image_opts, blk = img_open_opts(filename, opts, flags, writethrough, quiet, force_share); } else { - blk = img_open_file(filename, fmt, flags, writethrough, quiet, + blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet, force_share); } return blk; @@ -2286,8 +2318,8 @@ static int img_convert(int argc, char **argv) * That has to wait for bdrv_create to be improved * to allow filenames in option syntax */ - s.target = img_open_file(out_filename, out_fmt, flags, - writethrough, quiet, false); + s.target = img_open_new_file(out_filename, opts, out_fmt, + flags, writethrough, quiet, false); } if (!s.target) { ret = -1; @@ -4351,7 +4383,7 @@ static int img_dd(int argc, char **argv) * with the bdrv_create() call above which does not * support image-opts style. */ - blk2 = img_open_file(out.filename, out_fmt, BDRV_O_RDWR, + blk2 = img_open_file(out.filename, NULL, out_fmt, BDRV_O_RDWR, false, false, false); if (!blk2) { From adb998c12aa7aa22c78baaec5c1252721e89c3de Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Mon, 15 May 2017 22:10:14 +0800 Subject: [PATCH 07/10] qemu-img: Fix leakage of options on error Reported by Coverity. Signed-off-by: Fam Zheng Message-id: 20170515141014.25793-1-famz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- qemu-img.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qemu-img.c b/qemu-img.c index 0bf941ba56..5aef8ef047 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -295,6 +295,7 @@ static BlockBackend *img_open_opts(const char *optstr, if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) && !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) { error_report("--force-share/-U conflicts with image options"); + QDECREF(options); return NULL; } qdict_put(options, BDRV_OPT_FORCE_SHARE, qbool_from_bool(true)); From bcb07dba9290407eb01971ade287ca9a332ad49d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 8 May 2017 12:13:02 -0500 Subject: [PATCH 08/10] block: Tweak error message related to qemu-img amend When converting a 1.1 image down to 0.10, qemu-iotests 060 forces a contrived failure where allocating a cluster used to replace a zero cluster reads unaligned data. Since it is a zero cluster rather than a data cluster being converted, changing the error message to match our earlier change in 'qcow2: Make distinction between zero cluster types obvious' is worthwhile. Suggested-by: Max Reitz Signed-off-by: Eric Blake Message-id: 20170508171302.17805-1-eblake@redhat.com [mreitz: Commit message fixes] Signed-off-by: Max Reitz --- block/qcow2-cluster.c | 3 ++- tests/qemu-iotests/060.out | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 347d94b0d2..d779ea19cf 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1797,7 +1797,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } if (offset_into_cluster(s, offset)) { - qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset " + qcow2_signal_corruption(bs, true, -1, -1, + "Cluster allocation offset " "%#" PRIx64 " unaligned (L2 offset: %#" PRIx64 ", L2 index: %#x)", offset, l2_offset, j); diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 9e8f5b9d79..3bc14616be 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -143,7 +143,7 @@ read failed: Input/output error Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qcow2: Marking image as corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed +qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed qemu-img: Error while amending options: Input/output error === Testing unaligned reftable entry === From 0d54a6fed3ebaf0e17656a712e5d6575c712459b Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 22 May 2017 21:52:15 +0200 Subject: [PATCH 09/10] block: Fix backing paths for filenames with colons path_combine() naturally tries to preserve a protocol prefix. However, it recognizes such a prefix by scanning for the first colon; which is different from what path_has_protocol() does: There only is a protocol prefix if there is a colon before the first slash. A protocol prefix that is not recognized by path_has_protocol() is none, and should thus not be taken as one. Case in point, before this patch: $ ./qemu-img create -f qcow2 -b backing.qcow2 ./top:image.qcow2 qemu-img: ./top:image.qcow2: Could not open './top:backing.qcow2': No such file or directory Afterwards: $ ./qemu-img create -f qcow2 -b backing.qcow2 ./top:image.qcow2 qemu-img: ./top:image.qcow2: Could not open './backing.qcow2': No such file or directory Reported-by: yangyang Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20170522195217.12991-2-mreitz@redhat.com Signed-off-by: Max Reitz --- block.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/block.c b/block.c index 50ba264143..b72b872806 100644 --- a/block.c +++ b/block.c @@ -163,11 +163,16 @@ void path_combine(char *dest, int dest_size, if (path_is_absolute(filename)) { pstrcpy(dest, dest_size, filename); } else { - p = strchr(base_path, ':'); - if (p) - p++; - else - p = base_path; + const char *protocol_stripped = NULL; + + if (path_has_protocol(base_path)) { + protocol_stripped = strchr(base_path, ':'); + if (protocol_stripped) { + protocol_stripped++; + } + } + p = protocol_stripped ?: base_path; + p1 = strrchr(base_path, '/'); #ifdef _WIN32 { From 03c320d803fd881736b63015048498cf97d410d3 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 22 May 2017 21:52:16 +0200 Subject: [PATCH 10/10] block/file-*: *_parse_filename() and colons The file drivers' *_parse_filename() implementations just strip the optional protocol prefix off the filename. However, for e.g. "file:foo:bar", this would lead to "foo:bar" being stored as the BDS's filename which looks like it should be managed using the "foo" protocol. This is especially troublesome if you then try to resolve a backing filename based on "foo:bar". This issue can only occur if the stripped part is a relative filename ("file:/foo:bar" will be shortened to "/foo:bar" and having a slash before the first colon means that "/foo" is not recognized as a protocol part). Therefore, we can easily fix it by prepending "./" to such filenames. Before this patch: $ ./qemu-img create -f qcow2 backing.qcow2 64M Formatting 'backing.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 $ ./qemu-img create -f qcow2 -b backing.qcow2 file:top:image.qcow2 Formatting 'file:top:image.qcow2', fmt=qcow2 size=67108864 backing_file=backing.qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 $ ./qemu-io file:top:image.qcow2 can't open device file:top:image.qcow2: Could not open backing file: Unknown protocol 'top' After this patch: $ ./qemu-io file:top:image.qcow2 [no error] Signed-off-by: Max Reitz Message-id: 20170522195217.12991-3-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- block.c | 35 +++++++++++++++++++++++++++++++++++ block/file-posix.c | 17 +++-------------- block/file-win32.c | 12 ++---------- include/block/block_int.h | 3 +++ 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/block.c b/block.c index b72b872806..fa1d06d846 100644 --- a/block.c +++ b/block.c @@ -197,6 +197,41 @@ void path_combine(char *dest, int dest_size, } } +/* + * Helper function for bdrv_parse_filename() implementations to remove optional + * protocol prefixes (especially "file:") from a filename and for putting the + * stripped filename into the options QDict if there is such a prefix. + */ +void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, + QDict *options) +{ + if (strstart(filename, prefix, &filename)) { + /* Stripping the explicit protocol prefix may result in a protocol + * prefix being (wrongly) detected (if the filename contains a colon) */ + if (path_has_protocol(filename)) { + QString *fat_filename; + + /* This means there is some colon before the first slash; therefore, + * this cannot be an absolute path */ + assert(!path_is_absolute(filename)); + + /* And we can thus fix the protocol detection issue by prefixing it + * by "./" */ + fat_filename = qstring_from_str("./"); + qstring_append(fat_filename, filename); + + assert(!path_has_protocol(qstring_get_str(fat_filename))); + + qdict_put(options, "filename", fat_filename); + } else { + /* If no protocol prefix was detected, we can use the shortened + * filename as-is */ + qdict_put_str(options, "filename", filename); + } + } +} + + /* Returns whether the image file is opened as read-only. Note that this can * return false and writing to the image file is still not possible because the * image is inactivated. */ diff --git a/block/file-posix.c b/block/file-posix.c index 4354d49642..de2d3a2e3c 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -381,12 +381,7 @@ static void raw_parse_flags(int bdrv_flags, int *open_flags) static void raw_parse_filename(const char *filename, QDict *options, Error **errp) { - /* The filename does not have to be prefixed by the protocol name, since - * "file" is the default protocol; therefore, the return value of this - * function call can be ignored. */ - strstart(filename, "file:", &filename); - - qdict_put_str(options, "filename", filename); + bdrv_parse_filename_strip_prefix(filename, "file:", options); } static QemuOptsList raw_runtime_opts = { @@ -2395,10 +2390,7 @@ static int check_hdev_writable(BDRVRawState *s) static void hdev_parse_filename(const char *filename, QDict *options, Error **errp) { - /* The prefix is optional, just as for "file". */ - strstart(filename, "host_device:", &filename); - - qdict_put_str(options, "filename", filename); + bdrv_parse_filename_strip_prefix(filename, "host_device:", options); } static bool hdev_is_sg(BlockDriverState *bs) @@ -2697,10 +2689,7 @@ static BlockDriver bdrv_host_device = { static void cdrom_parse_filename(const char *filename, QDict *options, Error **errp) { - /* The prefix is optional, just as for "file". */ - strstart(filename, "host_cdrom:", &filename); - - qdict_put_str(options, "filename", filename); + bdrv_parse_filename_strip_prefix(filename, "host_cdrom:", options); } #endif diff --git a/block/file-win32.c b/block/file-win32.c index 8f14f0bdcd..ef2910b03f 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -276,12 +276,7 @@ static void raw_parse_flags(int flags, bool use_aio, int *access_flags, static void raw_parse_filename(const char *filename, QDict *options, Error **errp) { - /* The filename does not have to be prefixed by the protocol name, since - * "file" is the default protocol; therefore, the return value of this - * function call can be ignored. */ - strstart(filename, "file:", &filename); - - qdict_put_str(options, "filename", filename); + bdrv_parse_filename_strip_prefix(filename, "file:", options); } static QemuOptsList raw_runtime_opts = { @@ -671,10 +666,7 @@ static int hdev_probe_device(const char *filename) static void hdev_parse_filename(const char *filename, QDict *options, Error **errp) { - /* The prefix is optional, just as for "file". */ - strstart(filename, "host_device:", &filename); - - qdict_put_str(options, "filename", filename); + bdrv_parse_filename_strip_prefix(filename, "host_device:", options); } static int hdev_open(BlockDriverState *bs, QDict *options, int flags, diff --git a/include/block/block_int.h b/include/block/block_int.h index 8d3724cce6..e5eb473e53 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -682,6 +682,9 @@ int get_tmp_filename(char *filename, int size); BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, const char *filename); +void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, + QDict *options); + /** * bdrv_add_before_write_notifier: