X-Git-Url: https://git.proxmox.com/?p=mirror_qemu.git;a=blobdiff_plain;f=qemu-img.c;h=79983772de39e3e2b9f0d425a4043f28f0990716;hp=ae0025926c3516e1dfc0b84e3619de70924991ad;hb=c4107e8208d0222f9b328691b519aaee4101db87;hpb=1b967e9f348d48788a2ab481d45398b80ce71fa6 diff --git a/qemu-img.c b/qemu-img.c index ae0025926c..79983772de 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include +#include "qemu-common.h" #include "qemu-version.h" #include "qapi/error.h" #include "qapi/qapi-visit-block-core.h" @@ -37,6 +38,8 @@ #include "qemu/option.h" #include "qemu/error-report.h" #include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" #include "qom/object_interfaces.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" @@ -66,6 +69,7 @@ enum { OPTION_SIZE = 264, OPTION_PREALLOCATION = 265, OPTION_SHRINK = 266, + OPTION_SALVAGE = 267, }; typedef enum OutputFormat { @@ -85,13 +89,11 @@ static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) { va_list ap; - error_printf("qemu-img: "); - va_start(ap, fmt); - error_vprintf(fmt, ap); + error_vreport(fmt, ap); va_end(ap); - error_printf("\nTry 'qemu-img --help' for more information\n"); + error_printf("Try 'qemu-img --help' for more information\n"); exit(EXIT_FAILURE); } @@ -198,7 +200,7 @@ static void QEMU_NORETURN help(void) " 'skip=N' skip N bs-sized blocks at the start of input\n"; printf("%s\nSupported formats:", help_msg); - bdrv_iterate_format(format_print, NULL); + bdrv_iterate_format(format_print, NULL, false); printf("\n\n" QEMU_HELP_BOTTOM "\n"); exit(EXIT_SUCCESS); } @@ -269,9 +271,20 @@ static int print_block_option_help(const char *filename, const char *fmt) create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); } - printf("Supported options:\n"); + if (filename) { + printf("Supported options:\n"); + } else { + printf("Supported %s options:\n", fmt); + } qemu_opts_print_help(create_opts, false); qemu_opts_free(create_opts); + + if (!filename) { + printf("\n" + "The protocol level may support further options.\n" + "Specify the target filename to include those options.\n"); + } + return 0; } @@ -1207,7 +1220,7 @@ static int compare_buffers(const uint8_t *buf1, const uint8_t *buf2, return res; } -#define IO_BUF_SIZE (2 * 1024 * 1024) +#define IO_BUF_SIZE (2 * MiB) /* * Check if passed sectors are empty (not allocated or contain only 0 bytes) @@ -1569,6 +1582,8 @@ typedef struct ImgConvertState { int64_t target_backing_sectors; /* negative if unknown */ bool wr_in_order; bool copy_range; + bool salvage; + bool quiet; int min_sparse; int alignment; size_t cluster_sectors; @@ -1615,23 +1630,44 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) } if (s->sector_next_status <= sector_num) { - int64_t count = n * BDRV_SECTOR_SIZE; + uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE; + int64_t count; - if (s->target_has_backing) { + do { + count = n * BDRV_SECTOR_SIZE; + + if (s->target_has_backing) { + ret = bdrv_block_status(blk_bs(s->src[src_cur]), offset, + count, &count, NULL, NULL); + } else { + ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL, + offset, count, &count, NULL, + NULL); + } + + if (ret < 0) { + if (s->salvage) { + if (n == 1) { + if (!s->quiet) { + warn_report("error while reading block status at " + "offset %" PRIu64 ": %s", offset, + strerror(-ret)); + } + /* Just try to read the data, then */ + ret = BDRV_BLOCK_DATA; + count = BDRV_SECTOR_SIZE; + } else { + /* Retry on a shorter range */ + n = DIV_ROUND_UP(n, 4); + } + } else { + error_report("error while reading block status at offset " + "%" PRIu64 ": %s", offset, strerror(-ret)); + return ret; + } + } + } while (ret < 0); - ret = bdrv_block_status(blk_bs(s->src[src_cur]), - (sector_num - src_cur_offset) * - BDRV_SECTOR_SIZE, - count, &count, NULL, NULL); - } else { - ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL, - (sector_num - src_cur_offset) * - BDRV_SECTOR_SIZE, - count, &count, NULL, NULL); - } - if (ret < 0) { - return ret; - } n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); if (ret & BDRV_BLOCK_ZERO) { @@ -1668,15 +1704,15 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, int nb_sectors, uint8_t *buf) { + uint64_t single_read_until = 0; int n, ret; - QEMUIOVector qiov; - struct iovec iov; assert(nb_sectors <= s->buf_sectors); while (nb_sectors > 0) { BlockBackend *blk; int src_cur; int64_t bs_sectors, src_cur_offset; + uint64_t offset; /* In the case of compression with multiple source files, we can get a * nb_sectors that spreads into the next part. So we must be able to @@ -1685,16 +1721,29 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, blk = s->src[src_cur]; bs_sectors = s->src_sectors[src_cur]; + offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; + n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); - iov.iov_base = buf; - iov.iov_len = n << BDRV_SECTOR_BITS; - qemu_iovec_init_external(&qiov, &iov, 1); + if (single_read_until > offset) { + n = 1; + } - ret = blk_co_preadv( - blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, &qiov, 0); + ret = blk_co_pread(blk, offset, n << BDRV_SECTOR_BITS, buf, 0); if (ret < 0) { - return ret; + if (s->salvage) { + if (n > 1) { + single_read_until = offset + (n << BDRV_SECTOR_BITS); + continue; + } else { + if (!s->quiet) { + warn_report("error while reading offset %" PRIu64 + ": %s", offset, strerror(-ret)); + } + memset(buf, 0, BDRV_SECTOR_SIZE); + } + } else { + return ret; + } } sector_num += n; @@ -1711,8 +1760,6 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, enum ImgConvertBlockStatus status) { int ret; - QEMUIOVector qiov; - struct iovec iov; while (nb_sectors > 0) { int n = nb_sectors; @@ -1740,12 +1787,8 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, (s->compressed && !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE))) { - iov.iov_base = buf; - iov.iov_len = n << BDRV_SECTOR_BITS; - qemu_iovec_init_external(&qiov, &iov, 1); - - ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, &qiov, flags); + ret = blk_co_pwrite(s->target, sector_num << BDRV_SECTOR_BITS, + n << BDRV_SECTOR_BITS, buf, flags); if (ret < 0) { return ret; } @@ -1760,7 +1803,8 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, } ret = blk_co_pwrite_zeroes(s->target, sector_num << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, 0); + n << BDRV_SECTOR_BITS, + BDRV_REQ_MAY_UNMAP); if (ret < 0) { return ret; } @@ -1938,7 +1982,7 @@ static int convert_do_copy(ImgConvertState *s) if (!s->has_zero_init && !s->target_has_backing && bdrv_can_write_zeroes_with_unmap(blk_bs(s->target))) { - ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP); + ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK); if (ret == 0) { s->has_zero_init = true; } @@ -2008,7 +2052,7 @@ static int img_convert(int argc, char **argv) QDict *open_opts = NULL; char *options = NULL; Error *local_err = NULL; - bool writethrough, src_writethrough, quiet = false, image_opts = false, + bool writethrough, src_writethrough, image_opts = false, skip_create = false, progress = false, tgt_image_opts = false; int64_t ret = -EINVAL; bool force_share = false; @@ -2030,6 +2074,7 @@ static int img_convert(int argc, char **argv) {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"force-share", no_argument, 0, 'U'}, {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, + {"salvage", no_argument, 0, OPTION_SALVAGE}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", @@ -2116,7 +2161,7 @@ static int img_convert(int argc, char **argv) src_cache = optarg; break; case 'q': - quiet = true; + s.quiet = true; break; case 'n': skip_create = true; @@ -2147,6 +2192,9 @@ static int img_convert(int argc, char **argv) case OPTION_IMAGE_OPTS: image_opts = true; break; + case OPTION_SALVAGE: + s.salvage = true; + break; case OPTION_TARGET_IMAGE_OPTS: tgt_image_opts = true; break; @@ -2173,6 +2221,11 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } + if (s.copy_range && s.salvage) { + error_report("Cannot use copy offloading in salvaging mode"); + goto fail_getopt; + } + if (tgt_image_opts && !skip_create) { error_report("--target-image-opts requires use of -n flag"); goto fail_getopt; @@ -2205,7 +2258,7 @@ static int img_convert(int argc, char **argv) } /* Initialize before goto out */ - if (quiet) { + if (s.quiet) { progress = false; } qemu_progress_init(progress, 1.0); @@ -2216,7 +2269,7 @@ static int img_convert(int argc, char **argv) for (bs_i = 0; bs_i < s.src_num; bs_i++) { s.src[bs_i] = img_open(image_opts, argv[optind + bs_i], - fmt, src_flags, src_writethrough, quiet, + fmt, src_flags, src_writethrough, s.quiet, force_share); if (!s.src[bs_i]) { ret = -1; @@ -2379,7 +2432,7 @@ static int img_convert(int argc, char **argv) if (skip_create) { s.target = img_open(tgt_image_opts, out_filename, out_fmt, - flags, writethrough, quiet, false); + flags, writethrough, s.quiet, false); } else { /* TODO ultimately we should allow --target-image-opts * to be used even when -n is not given. @@ -2387,7 +2440,7 @@ static int img_convert(int argc, char **argv) * to allow filenames in option syntax */ s.target = img_open_file(out_filename, open_opts, out_fmt, - flags, writethrough, quiet, false); + flags, writethrough, s.quiet, false); open_opts = NULL; /* blk_new_open will have freed it */ } if (!s.target) { @@ -2488,11 +2541,11 @@ static void dump_snapshots(BlockDriverState *bs) if (nb_sns <= 0) return; printf("Snapshot list:\n"); - bdrv_snapshot_dump(fprintf, stdout, NULL); + bdrv_snapshot_dump(NULL); printf("\n"); for(i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; - bdrv_snapshot_dump(fprintf, stdout, sn); + bdrv_snapshot_dump(sn); printf("\n"); } g_free(sn_tab); @@ -2541,7 +2594,7 @@ static void dump_human_image_info_list(ImageInfoList *list) } delim = true; - bdrv_image_info_dump(fprintf, stdout, elem->value); + bdrv_image_info_dump(elem->value); } } @@ -2740,14 +2793,14 @@ static int img_info(int argc, char **argv) return 0; } -static void dump_map_entry(OutputFormat output_format, MapEntry *e, - MapEntry *next) +static int dump_map_entry(OutputFormat output_format, MapEntry *e, + MapEntry *next) { switch (output_format) { case OFORMAT_HUMAN: if (e->data && !e->has_offset) { error_report("File contains external, encrypted or compressed clusters."); - exit(1); + return -1; } if (e->data && !e->zero) { printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", @@ -2780,6 +2833,7 @@ static void dump_map_entry(OutputFormat output_format, MapEntry *e, } break; } + return 0; } static int get_block_status(BlockDriverState *bs, int64_t offset, @@ -2958,7 +3012,7 @@ static int img_map(int argc, char **argv) int64_t n; /* Probe up to 1 GiB at a time. */ - n = MIN(1 << 30, length - offset); + n = MIN(1 * GiB, length - offset); ret = get_block_status(bs, offset, n, &next); if (ret < 0) { @@ -2972,12 +3026,15 @@ static int img_map(int argc, char **argv) } if (curr.length > 0) { - dump_map_entry(output_format, &curr, &next); + ret = dump_map_entry(output_format, &curr, &next); + if (ret < 0) { + goto out; + } } curr = next; } - dump_map_entry(output_format, &curr, NULL); + ret = dump_map_entry(output_format, &curr, NULL); out: blk_unref(blk); @@ -3158,7 +3215,7 @@ static int img_rebase(int argc, char **argv) BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; uint8_t *buf_old = NULL; uint8_t *buf_new = NULL; - BlockDriverState *bs = NULL; + BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; @@ -3303,29 +3360,23 @@ static int img_rebase(int argc, char **argv) /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { - char backing_name[PATH_MAX]; QDict *options = NULL; - - if (bs->backing_format[0] != '\0') { - options = qdict_new(); - qdict_put_str(options, "driver", bs->backing_format); - } - - if (force_share) { - if (!options) { - options = qdict_new(); + BlockDriverState *base_bs = backing_bs(bs); + + if (base_bs) { + blk_old_backing = blk_new(qemu_get_aio_context(), + BLK_PERM_CONSISTENT_READ, + BLK_PERM_ALL); + ret = blk_insert_bs(blk_old_backing, base_bs, + &local_err); + if (ret < 0) { + error_reportf_err(local_err, + "Could not reuse old backing file '%s': ", + base_bs->filename); + goto out; } - qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); - } - bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - blk_old_backing = blk_new_open(backing_name, NULL, - options, src_flags, &local_err); - if (!blk_old_backing) { - error_reportf_err(local_err, - "Could not open old backing file '%s': ", - backing_name); - ret = -1; - goto out; + } else { + blk_old_backing = NULL; } if (out_baseimg[0]) { @@ -3348,21 +3399,44 @@ static int img_rebase(int argc, char **argv) out_baseimg, &local_err); if (local_err) { + qobject_unref(options); error_reportf_err(local_err, "Could not resolve backing filename: "); ret = -1; goto out; } - blk_new_backing = blk_new_open(out_real_path, NULL, - options, src_flags, &local_err); - g_free(out_real_path); - if (!blk_new_backing) { - error_reportf_err(local_err, - "Could not open new backing file '%s': ", - out_baseimg); - ret = -1; - goto out; + /* + * Find out whether we rebase an image on top of a previous image + * in its chain. + */ + prefix_chain_bs = bdrv_find_backing_image(bs, out_real_path); + if (prefix_chain_bs) { + qobject_unref(options); + g_free(out_real_path); + + blk_new_backing = blk_new(qemu_get_aio_context(), + BLK_PERM_CONSISTENT_READ, + BLK_PERM_ALL); + ret = blk_insert_bs(blk_new_backing, prefix_chain_bs, + &local_err); + if (ret < 0) { + error_reportf_err(local_err, + "Could not reuse backing file '%s': ", + out_baseimg); + goto out; + } + } else { + blk_new_backing = blk_new_open(out_real_path, NULL, + options, src_flags, &local_err); + g_free(out_real_path); + if (!blk_new_backing) { + error_reportf_err(local_err, + "Could not open new backing file '%s': ", + out_baseimg); + ret = -1; + goto out; + } } } } @@ -3378,7 +3452,7 @@ static int img_rebase(int argc, char **argv) */ if (!unsafe) { int64_t size; - int64_t old_backing_size; + int64_t old_backing_size = 0; int64_t new_backing_size = 0; uint64_t offset; int64_t n; @@ -3394,15 +3468,18 @@ static int img_rebase(int argc, char **argv) ret = -1; goto out; } - old_backing_size = blk_getlength(blk_old_backing); - if (old_backing_size < 0) { - char backing_name[PATH_MAX]; + if (blk_old_backing) { + old_backing_size = blk_getlength(blk_old_backing); + if (old_backing_size < 0) { + char backing_name[PATH_MAX]; - bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - error_report("Could not get size of '%s': %s", - backing_name, strerror(-old_backing_size)); - ret = -1; - goto out; + bdrv_get_backing_filename(bs, backing_name, + sizeof(backing_name)); + error_report("Could not get size of '%s': %s", + backing_name, strerror(-old_backing_size)); + ret = -1; + goto out; + } } if (blk_new_backing) { new_backing_size = blk_getlength(blk_new_backing); @@ -3419,6 +3496,8 @@ static int img_rebase(int argc, char **argv) } for (offset = 0; offset < size; offset += n) { + bool buf_old_is_zero = false; + /* How many bytes can we handle with the next read? */ n = MIN(IO_BUF_SIZE, size - offset); @@ -3433,12 +3512,30 @@ static int img_rebase(int argc, char **argv) continue; } + if (prefix_chain_bs) { + /* + * If cluster wasn't changed since prefix_chain, we don't need + * to take action + */ + ret = bdrv_is_allocated_above(backing_bs(bs), prefix_chain_bs, + false, offset, n, &n); + if (ret < 0) { + error_report("error while reading image metadata: %s", + strerror(-ret)); + goto out; + } + if (!ret) { + continue; + } + } + /* * Read old and new backing file and take into consideration that * backing files may be smaller than the COW image. */ if (offset >= old_backing_size) { memset(buf_old, 0, n); + buf_old_is_zero = true; } else { if (offset + n > old_backing_size) { n = old_backing_size - offset; @@ -3474,8 +3571,12 @@ static int img_rebase(int argc, char **argv) if (compare_buffers(buf_old + written, buf_new + written, n - written, &pnum)) { - ret = blk_pwrite(blk, offset + written, - buf_old + written, pnum, 0); + if (buf_old_is_zero) { + ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); + } else { + ret = blk_pwrite(blk, offset + written, + buf_old + written, pnum, 0); + } if (ret < 0) { error_report("Error while writing to COW image: %s", strerror(-ret)); @@ -4922,8 +5023,8 @@ int main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); #endif + error_init(argv[0]); module_call_init(MODULE_INIT_TRACE); - error_set_progname(argv[0]); qemu_init_exec_dir(argv[0]); if (qemu_init_main_loop(&local_error)) {