#include "crypto/init.h"
#include "trace/control.h"
-#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \
+#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
"\n" QEMU_COPYRIGHT "\n"
typedef struct img_cmd_t {
" " arg_string "\n"
#include "qemu-img-cmds.h"
#undef DEF
-#undef GEN_DOCS
"\n"
"Command parameters:\n"
" 'filename' is a disk image filename\n"
options = qemu_opts_to_qdict(opts, NULL);
if (force_share) {
if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE)
- && !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) {
+ && strcmp(qdict_get_str(options, BDRV_OPT_FORCE_SHARE), "on")) {
error_report("--force-share/-U conflicts with image options");
- QDECREF(options);
+ qobject_unref(options);
return NULL;
}
- qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
+ qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on");
}
blk = blk_new_open(NULL, NULL, options, flags, &local_err);
if (!blk) {
str = qobject_to_json_pretty(obj);
assert(str != NULL);
qprintf(quiet, "%s\n", qstring_get_str(str));
- qobject_decref(obj);
+ qobject_unref(obj);
visit_free(v);
- QDECREF(str);
+ qobject_unref(str);
}
static void dump_human_image_check(ImageCheck *check, bool quiet)
int ret = 0;
aio_context_acquire(aio_context);
- block_job_ref(job);
+ job_ref(&job->job);
do {
+ float progress = 0.0f;
aio_poll(aio_context, true);
- qemu_progress_print(job->len ?
- ((float)job->offset / job->len * 100.f) : 0.0f, 0);
- } while (!job->ready && !job->completed);
+ if (job->job.progress_total) {
+ progress = (float)job->job.progress_current /
+ job->job.progress_total * 100.f;
+ }
+ qemu_progress_print(progress, 0);
+ } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
- if (!job->completed) {
- ret = block_job_complete_sync(job, errp);
+ if (!job_is_completed(&job->job)) {
+ ret = job_complete_sync(&job->job, errp);
} else {
- ret = job->ret;
+ ret = job->job.ret;
}
- block_job_unref(job);
+ job_unref(&job->job);
aio_context_release(aio_context);
/* publish completion progress only when success */
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0,
+ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0,
BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
&cbi, false, &local_err);
aio_context_release(aio_context);
bool compressed;
bool target_has_backing;
bool wr_in_order;
+ bool copy_range;
int min_sparse;
size_t cluster_sectors;
size_t buf_sectors;
return 0;
}
+static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector_num,
+ int nb_sectors)
+{
+ int n, ret;
+
+ while (nb_sectors > 0) {
+ BlockBackend *blk;
+ int src_cur;
+ int64_t bs_sectors, src_cur_offset;
+ int64_t offset;
+
+ convert_select_part(s, sector_num, &src_cur, &src_cur_offset);
+ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS;
+ blk = s->src[src_cur];
+ bs_sectors = s->src_sectors[src_cur];
+
+ n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
+
+ ret = blk_co_copy_range(blk, offset, s->target,
+ sector_num << BDRV_SECTOR_BITS,
+ n << BDRV_SECTOR_BITS, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ sector_num += n;
+ nb_sectors -= n;
+ }
+ return 0;
+}
+
static void coroutine_fn convert_co_do_copy(void *opaque)
{
ImgConvertState *s = opaque;
int n;
int64_t sector_num;
enum ImgConvertBlockStatus status;
+ bool copy_range;
qemu_co_mutex_lock(&s->lock);
if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) {
s->allocated_sectors, 0);
}
- if (status == BLK_DATA) {
+retry:
+ copy_range = s->copy_range && s->status == BLK_DATA;
+ if (status == BLK_DATA && !copy_range) {
ret = convert_co_read(s, sector_num, n, buf);
if (ret < 0) {
error_report("error while reading sector %" PRId64
}
if (s->ret == -EINPROGRESS) {
- ret = convert_co_write(s, sector_num, n, buf, status);
+ if (copy_range) {
+ ret = convert_co_copy_range(s, sector_num, n);
+ if (ret) {
+ s->copy_range = false;
+ goto retry;
+ }
+ } else {
+ ret = convert_co_write(s, sector_num, n, buf, status);
+ }
if (ret < 0) {
error_report("error while writing sector %" PRId64
": %s", sector_num, strerror(-ret));
ImgConvertState s = (ImgConvertState) {
/* Need at least 4k of zeros for sparse detection */
.min_sparse = 8,
+ .copy_range = true,
.buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE,
.wr_in_order = true,
.num_coroutines = 8,
break;
case 'c':
s.compressed = true;
+ s.copy_range = false;
break;
case 'o':
if (!is_valid_option_list(optarg)) {
}
s.min_sparse = sval / BDRV_SECTOR_SIZE;
+ s.copy_range = false;
break;
}
case 'p':
str = qobject_to_json_pretty(obj);
assert(str != NULL);
printf("%s\n", qstring_get_str(str));
- qobject_decref(obj);
+ qobject_unref(obj);
visit_free(v);
- QDECREF(str);
+ qobject_unref(str);
}
static void dump_json_image_info(ImageInfo *info)
str = qobject_to_json_pretty(obj);
assert(str != NULL);
printf("%s\n", qstring_get_str(str));
- qobject_decref(obj);
+ qobject_unref(obj);
visit_free(v);
- QDECREF(str);
+ qobject_unref(str);
}
static void dump_human_image_info_list(ImageInfoList *list)
Error *err = NULL;
int c, ret, relative;
const char *filename, *fmt, *size;
- int64_t n, total_size, current_size;
+ int64_t n, total_size, current_size, new_size;
bool quiet = false;
BlockBackend *blk = NULL;
PreallocMode prealloc = PREALLOC_MODE_OFF;
}
}
if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ error_exit("Expecting image file name and size");
}
filename = argv[optind++];
}
ret = blk_truncate(blk, total_size, prealloc, &err);
- if (!ret) {
- qprintf(quiet, "Image resized.\n");
- } else {
+ if (ret < 0) {
error_report_err(err);
+ goto out;
}
+
+ new_size = blk_getlength(blk);
+ if (new_size < 0) {
+ error_report("Failed to verify truncated image length: %s",
+ strerror(-new_size));
+ ret = -1;
+ goto out;
+ }
+
+ /* Some block drivers implement a truncation method, but only so
+ * the user can cause qemu to refresh the image's size from disk.
+ * The idea is that the user resizes the image outside of qemu and
+ * then invokes block_resize to inform qemu about it.
+ * (This includes iscsi and file-posix for device files.)
+ * Of course, that is not the behavior someone invoking
+ * qemu-img resize would find useful, so we catch that behavior
+ * here and tell the user. */
+ if (new_size != total_size && new_size == current_size) {
+ error_report("Image was not resized; resizing may not be supported "
+ "for this image");
+ ret = -1;
+ goto out;
+ }
+
+ if (new_size != total_size) {
+ warn_report("Image should have been resized to %" PRIi64
+ " bytes, but was resized to %" PRIi64 " bytes",
+ total_size, new_size);
+ }
+
+ qprintf(quiet, "Image resized.\n");
+
out:
blk_unref(blk);
if (ret) {
str = qobject_to_json_pretty(obj);
assert(str != NULL);
printf("%s\n", qstring_get_str(str));
- qobject_decref(obj);
+ qobject_unref(obj);
visit_free(v);
- QDECREF(str);
+ qobject_unref(str);
}
static int img_measure(int argc, char **argv)
{ option, callback },
#include "qemu-img-cmds.h"
#undef DEF
-#undef GEN_DOCS
{ NULL, NULL, },
};