#include "qemu/osdep.h"
#include <getopt.h>
-#include "qemu-common.h"
+#include "qemu/help-texts.h"
+#include "qemu/qemu-progress.h"
#include "qemu-version.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-block-core.h"
#include "sysemu/block-backend.h"
#include "block/block_int.h"
#include "block/blockjob.h"
+#include "block/dirty-bitmap.h"
#include "block/qapi.h"
#include "crypto/init.h"
#include "trace/control.h"
printf(" %s", name);
}
-static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...)
+static G_NORETURN G_GNUC_PRINTF(1, 2)
+void error_exit(const char *fmt, ...)
{
va_list ap;
exit(EXIT_FAILURE);
}
-static void QEMU_NORETURN missing_argument(const char *option)
+static G_NORETURN
+void missing_argument(const char *option)
{
error_exit("missing argument for option '%s'", option);
}
-static void QEMU_NORETURN unrecognized_option(const char *option)
+static G_NORETURN
+void unrecognized_option(const char *option)
{
error_exit("unrecognized option '%s'", option);
}
/* Please keep in synch with docs/tools/qemu-img.rst */
-static void QEMU_NORETURN help(void)
+static G_NORETURN
+void help(void)
{
const char *help_msg =
QEMU_IMG_VERSION
" 'output_filename' is the destination disk image filename\n"
" 'output_fmt' is the destination format\n"
" 'options' is a comma separated list of format specific options in a\n"
- " name=value format. Use -o ? for an overview of the options supported by the\n"
- " used format\n"
+ " name=value format. Use -o help for an overview of the options supported by\n"
+ " the used format\n"
" 'snapshot_param' is param used for internal snapshot, format\n"
" is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
" '[ID_OR_NAME]'\n"
},
};
-static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...)
+static int G_GNUC_PRINTF(2, 3) qprintf(bool quiet, const char *fmt, ...)
{
int ret = 0;
if (!quiet) {
blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet,
force_share);
}
+
+ if (blk) {
+ blk_set_force_allow_inactivate(blk);
+ }
+
return blk;
}
AioContext *aio_context = block_job_get_aio_context(job);
int ret = 0;
- aio_context_acquire(aio_context);
- job_ref(&job->job);
+ job_lock();
+ job_ref_locked(&job->job);
do {
float progress = 0.0f;
+ job_unlock();
aio_poll(aio_context, true);
progress_get_snapshot(&job->job.progress, &progress_current,
progress = (float)progress_current / progress_total * 100.f;
}
qemu_progress_print(progress, 0);
- } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
+ job_lock();
+ } while (!job_is_ready_locked(&job->job) &&
+ !job_is_completed_locked(&job->job));
- if (!job_is_completed(&job->job)) {
- ret = job_complete_sync(&job->job, errp);
+ if (!job_is_completed_locked(&job->job)) {
+ ret = job_complete_sync_locked(&job->job, errp);
} else {
ret = job->job.ret;
}
- job_unref(&job->job);
- aio_context_release(aio_context);
+ job_unref_locked(&job->job);
+ job_unlock();
/* publish completion progress only when success */
if (!ret) {
done:
qemu_progress_end();
+ /*
+ * Manually inactivate the image first because this way we can know whether
+ * an error occurred. blk_unref() doesn't tell us about failures.
+ */
+ ret = bdrv_inactivate_all();
+ if (ret < 0 && !local_err) {
+ error_setg_errno(&local_err, -ret, "Error while closing the image");
+ }
blk_unref(blk);
if (local_err) {
int ret = 0;
int64_t idx;
- ret = blk_pread(blk, offset, buffer, bytes);
+ ret = blk_pread(blk, offset, bytes, buffer, 0);
if (ret < 0) {
error_report("Error while reading offset %" PRId64 " of %s: %s",
offset, filename, strerror(-ret));
int64_t pnum;
chunk = MIN(chunk, IO_BUF_SIZE);
- ret = blk_pread(blk1, offset, buf1, chunk);
+ ret = blk_pread(blk1, offset, chunk, buf1, 0);
if (ret < 0) {
error_report("Error while reading offset %" PRId64
" of %s: %s",
ret = 4;
goto out;
}
- ret = blk_pread(blk2, offset, buf2, chunk);
+ ret = blk_pread(blk2, offset, chunk, buf2, 0);
if (ret < 0) {
error_report("Error while reading offset %" PRId64
" of %s: %s",
const char *src_node, const char *src_name,
Error **errp)
{
- BlockDirtyBitmapMergeSource *merge_src;
- BlockDirtyBitmapMergeSourceList *list = NULL;
+ BlockDirtyBitmapOrStr *merge_src;
+ BlockDirtyBitmapOrStrList *list = NULL;
- merge_src = g_new0(BlockDirtyBitmapMergeSource, 1);
+ merge_src = g_new0(BlockDirtyBitmapOrStr, 1);
merge_src->type = QTYPE_QDICT;
merge_src->u.external.node = g_strdup(src_node);
merge_src->u.external.name = g_strdup(src_name);
QAPI_LIST_PREPEND(list, merge_src);
qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp);
- qapi_free_BlockDirtyBitmapMergeSourceList(list);
+ qapi_free_BlockDirtyBitmapOrStrList(list);
}
enum ImgConvertBlockStatus {
if (s->compressed && !s->ret) {
/* signal EOF to align */
- ret = blk_pwrite_compressed(s->target, 0, NULL, 0);
+ ret = blk_pwrite_compressed(s->target, 0, 0, NULL);
if (ret < 0) {
return ret;
}
g_free(sn_tab);
}
-static void dump_json_image_info_list(ImageInfoList *list)
+static void dump_json_block_graph_info_list(BlockGraphInfoList *list)
{
GString *str;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
- visit_type_ImageInfoList(v, NULL, &list, &error_abort);
+ visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort);
visit_complete(v, &obj);
str = qobject_to_json_pretty(obj, true);
assert(str != NULL);
g_string_free(str, true);
}
-static void dump_json_image_info(ImageInfo *info)
+static void dump_json_block_graph_info(BlockGraphInfo *info)
{
GString *str;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
- visit_type_ImageInfo(v, NULL, &info, &error_abort);
+ visit_type_BlockGraphInfo(v, NULL, &info, &error_abort);
visit_complete(v, &obj);
str = qobject_to_json_pretty(obj, true);
assert(str != NULL);
g_string_free(str, true);
}
-static void dump_human_image_info_list(ImageInfoList *list)
+static void dump_human_image_info(BlockGraphInfo *info, int indentation,
+ const char *path)
+{
+ BlockChildInfoList *children_list;
+
+ bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation,
+ info->children == NULL);
+
+ for (children_list = info->children; children_list;
+ children_list = children_list->next)
+ {
+ BlockChildInfo *child = children_list->value;
+ g_autofree char *child_path = NULL;
+
+ printf("%*sChild node '%s%s':\n",
+ indentation * 4, "", path, child->name);
+ child_path = g_strdup_printf("%s%s/", path, child->name);
+ dump_human_image_info(child->info, indentation + 1, child_path);
+ }
+}
+
+static void dump_human_image_info_list(BlockGraphInfoList *list)
{
- ImageInfoList *elem;
+ BlockGraphInfoList *elem;
bool delim = false;
for (elem = list; elem; elem = elem->next) {
}
delim = true;
- bdrv_image_info_dump(elem->value);
+ dump_human_image_info(elem->value, 0, "/");
}
}
}
/**
- * Open an image file chain and return an ImageInfoList
+ * Open an image file chain and return an BlockGraphInfoList
*
* @filename: topmost image filename
* @fmt: topmost image format (may be NULL to autodetect)
* @chain: true - enumerate entire backing file chain
* false - only topmost image file
*
- * Returns a list of ImageInfo objects or NULL if there was an error opening an
- * image file. If there was an error a message will have been printed to
- * stderr.
+ * Returns a list of BlockNodeInfo objects or NULL if there was an error
+ * opening an image file. If there was an error a message will have been
+ * printed to stderr.
*/
-static ImageInfoList *collect_image_info_list(bool image_opts,
- const char *filename,
- const char *fmt,
- bool chain, bool force_share)
+static BlockGraphInfoList *collect_image_info_list(bool image_opts,
+ const char *filename,
+ const char *fmt,
+ bool chain, bool force_share)
{
- ImageInfoList *head = NULL;
- ImageInfoList **tail = &head;
+ BlockGraphInfoList *head = NULL;
+ BlockGraphInfoList **tail = &head;
GHashTable *filenames;
Error *err = NULL;
while (filename) {
BlockBackend *blk;
BlockDriverState *bs;
- ImageInfo *info;
+ BlockGraphInfo *info;
if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
error_report("Backing file '%s' creates an infinite loop.",
}
bs = blk_bs(blk);
- bdrv_query_image_info(bs, &info, &err);
+ /*
+ * Note that the returned BlockGraphInfo object will not have
+ * information about this image's backing node, because we have opened
+ * it with BDRV_O_NO_BACKING. Printing this object will therefore not
+ * duplicate the backing chain information that we obtain by walking
+ * the chain manually here.
+ */
+ bdrv_query_block_graph_info(bs, &info, &err);
if (err) {
error_report_err(err);
blk_unref(blk);
image_opts = false;
if (chain) {
- if (info->has_full_backing_filename) {
+ if (info->full_backing_filename) {
filename = info->full_backing_filename;
- } else if (info->has_backing_filename) {
+ } else if (info->backing_filename) {
error_report("Could not determine absolute backing filename,"
" but backing filename '%s' present",
info->backing_filename);
goto err;
}
- if (info->has_backing_filename_format) {
+ if (info->backing_filename_format) {
fmt = info->backing_filename_format;
}
}
return head;
err:
- qapi_free_ImageInfoList(head);
+ qapi_free_BlockGraphInfoList(head);
g_hash_table_destroy(filenames);
return NULL;
}
OutputFormat output_format = OFORMAT_HUMAN;
bool chain = false;
const char *filename, *fmt, *output;
- ImageInfoList *list;
+ BlockGraphInfoList *list;
bool image_opts = false;
bool force_share = false;
break;
case OFORMAT_JSON:
if (chain) {
- dump_json_image_info_list(list);
+ dump_json_block_graph_info_list(list);
} else {
- dump_json_image_info(list->value);
+ dump_json_block_graph_info(list->value);
}
break;
}
- qapi_free_ImageInfoList(list);
+ qapi_free_BlockGraphInfoList(list);
return 0;
}
printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n",
e->start, e->length,
e->has_offset ? e->offset : 0,
- e->has_filename ? e->filename : "");
+ e->filename ?: "");
}
/* This format ignores the distinction between 0, ZERO and ZERO|DATA.
* Modify the flags here to allow more coalescing.
.has_offset = has_offset,
.depth = depth,
.present = !!(ret & BDRV_BLOCK_ALLOCATED),
- .has_filename = filename,
.filename = filename,
};
curr->data != next->data ||
curr->depth != next->depth ||
curr->present != next->present ||
- curr->has_filename != next->has_filename ||
+ !curr->filename != !next->filename ||
curr->has_offset != next->has_offset) {
return false;
}
- if (curr->has_filename && strcmp(curr->filename, next->filename)) {
+ if (curr->filename && strcmp(curr->filename, next->filename)) {
return false;
}
if (curr->has_offset && curr->offset + curr->length != next->offset) {
char *filename, *snapshot_name = NULL;
int c, ret = 0, bdrv_oflags;
int action = 0;
- qemu_timeval tv;
bool quiet = false;
Error *err = NULL;
bool image_opts = false;
bool force_share = false;
+ int64_t rt;
bdrv_oflags = BDRV_O_RDWR;
/* Parse commandline parameters */
memset(&sn, 0, sizeof(sn));
pstrcpy(sn.name, sizeof(sn.name), snapshot_name);
- qemu_gettimeofday(&tv);
- sn.date_sec = tv.tv_sec;
- sn.date_nsec = tv.tv_usec * 1000;
+ rt = g_get_real_time();
+ sn.date_sec = rt / G_USEC_PER_SEC;
+ sn.date_nsec = (rt % G_USEC_PER_SEC) * 1000;
ret = bdrv_snapshot_create(bs, &sn);
if (ret) {
n = old_backing_size - offset;
}
- ret = blk_pread(blk_old_backing, offset, buf_old, n);
+ ret = blk_pread(blk_old_backing, offset, n, buf_old, 0);
if (ret < 0) {
error_report("error while reading from old backing file");
goto out;
n = new_backing_size - offset;
}
- ret = blk_pread(blk_new_backing, offset, buf_new, n);
+ ret = blk_pread(blk_new_backing, offset, n, buf_new, 0);
if (ret < 0) {
error_report("error while reading from new backing file");
goto out;
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);
+ ret = blk_pwrite(blk, offset + written, pnum,
+ buf_old + written, 0);
}
if (ret < 0) {
error_report("Error while writing to COW image: %s",
struct timeval t1, t2;
int i;
bool force_share = false;
- size_t buf_size;
+ size_t buf_size = 0;
for (;;) {
static const struct option long_options[] = {
data.buf = blk_blockalign(blk, buf_size);
memset(data.buf, pattern, data.nrreq * data.bufsize);
- blk_register_buf(blk, data.buf, buf_size);
+ blk_register_buf(blk, data.buf, buf_size, &error_fatal);
data.qiov = g_new(QEMUIOVector, data.nrreq);
for (i = 0; i < data.nrreq; i++) {
out:
if (data.buf) {
- blk_unregister_buf(blk, data.buf);
+ blk_unregister_buf(blk, data.buf, buf_size);
}
qemu_vfree(data.buf);
blk_unref(blk);
QSIMPLEQ_HEAD(, ImgBitmapAction) actions;
ImgBitmapAction *act, *act_next;
const char *op;
+ int inactivate_ret;
QSIMPLEQ_INIT(&actions);
ret = 0;
out:
+ /*
+ * Manually inactivate the images first because this way we can know whether
+ * an error occurred. blk_unref() doesn't tell us about failures.
+ */
+ inactivate_ret = bdrv_inactivate_all();
+ if (inactivate_ret < 0) {
+ error_report("Error while closing the image: %s", strerror(-inactivate_ret));
+ ret = 1;
+ }
+
blk_unref(src);
blk_unref(blk);
qemu_opts_del(opts);
const char *out_fmt = "raw";
const char *fmt = NULL;
int64_t size = 0;
- int64_t block_count = 0, out_pos, in_pos;
+ int64_t out_pos, in_pos;
bool force_share = false;
struct DdInfo dd = {
.flags = 0,
in.buf = g_new(uint8_t, in.bsz);
- for (out_pos = 0; in_pos < size; block_count++) {
- int in_ret, out_ret;
+ for (out_pos = 0; in_pos < size; ) {
+ int bytes = (in_pos + in.bsz > size) ? size - in_pos : in.bsz;
- if (in_pos + in.bsz > size) {
- in_ret = blk_pread(blk1, in_pos, in.buf, size - in_pos);
- } else {
- in_ret = blk_pread(blk1, in_pos, in.buf, in.bsz);
- }
- if (in_ret < 0) {
+ ret = blk_pread(blk1, in_pos, bytes, in.buf, 0);
+ if (ret < 0) {
error_report("error while reading from input image file: %s",
- strerror(-in_ret));
- ret = -1;
+ strerror(-ret));
goto out;
}
- in_pos += in_ret;
+ in_pos += bytes;
- out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0);
-
- if (out_ret < 0) {
+ ret = blk_pwrite(blk2, out_pos, bytes, in.buf, 0);
+ if (ret < 0) {
error_report("error while writing to output image file: %s",
- strerror(-out_ret));
- ret = -1;
+ strerror(-ret));
goto out;
}
- out_pos += out_ret;
+ out_pos += bytes;
}
out:
exit(1);
}
trace_init_file();
- qemu_set_log(LOG_TRACE);
+ qemu_set_log(LOG_TRACE, &error_fatal);
/* find the command */
for (cmd = img_cmds; cmd->name != NULL; cmd++) {