#include "block/snapshot.h"
#include "block/block_int.h"
#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
+#include "qapi/qmp/qstring.h"
+#include "qemu/option.h"
QemuOptsList internal_snapshot_opts = {
.name = "snapshot",
}
int bdrv_snapshot_goto(BlockDriverState *bs,
- const char *snapshot_id)
+ const char *snapshot_id,
+ Error **errp)
{
BlockDriver *drv = bs->drv;
int ret, open_ret;
if (!drv) {
+ error_setg(errp, "Block driver is closed");
return -ENOMEDIUM;
}
+
+ if (!QLIST_EMPTY(&bs->dirty_bitmaps)) {
+ error_setg(errp, "Device has active dirty bitmaps");
+ return -EBUSY;
+ }
+
if (drv->bdrv_snapshot_goto) {
- return drv->bdrv_snapshot_goto(bs, snapshot_id);
+ ret = drv->bdrv_snapshot_goto(bs, snapshot_id);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to load snapshot");
+ }
+ return ret;
}
if (bs->file) {
+ BlockDriverState *file;
+ QDict *options = qdict_clone_shallow(bs->options);
+ QDict *file_options;
+ Error *local_err = NULL;
+
+ file = bs->file->bs;
+ /* Prevent it from getting deleted when detached from bs */
+ bdrv_ref(file);
+
+ qdict_extract_subqdict(options, &file_options, "file.");
+ QDECREF(file_options);
+ qdict_put_str(options, "file", bdrv_get_node_name(file));
+
drv->bdrv_close(bs);
- ret = bdrv_snapshot_goto(bs->file->bs, snapshot_id);
- open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL);
+ bdrv_unref_child(bs, bs->file);
+ bs->file = NULL;
+
+ ret = bdrv_snapshot_goto(file, snapshot_id, errp);
+ open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
+ QDECREF(options);
if (open_ret < 0) {
- bdrv_unref(bs->file->bs);
+ bdrv_unref(file);
bs->drv = NULL;
- return open_ret;
+ /* A bdrv_snapshot_goto() error takes precedence */
+ error_propagate(errp, local_err);
+ return ret < 0 ? ret : open_ret;
}
+
+ assert(bs->file->bs == file);
+ bdrv_unref(file);
return ret;
}
+ error_setg(errp, "Block driver does not support snapshots");
return -ENOTSUP;
}
ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err);
}
- if (local_err) {
- error_propagate(errp, local_err);
- }
+ error_propagate(errp, local_err);
return ret;
}
bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs)
{
bool ok = true;
- BlockDriverState *bs = NULL;
+ BlockDriverState *bs;
+ BdrvNextIterator it;
- while (ok && (bs = bdrv_next(bs))) {
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
ok = bdrv_can_snapshot(bs);
}
aio_context_release(ctx);
+ if (!ok) {
+ bdrv_next_cleanup(&it);
+ goto fail;
+ }
}
+fail:
*first_bad_bs = bs;
return ok;
}
Error **err)
{
int ret = 0;
- BlockDriverState *bs = NULL;
+ BlockDriverState *bs;
+ BdrvNextIterator it;
QEMUSnapshotInfo sn1, *snapshot = &sn1;
- while (ret == 0 && (bs = bdrv_next(bs))) {
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
ret = bdrv_snapshot_delete_by_id_or_name(bs, name, err);
}
aio_context_release(ctx);
+ if (ret < 0) {
+ bdrv_next_cleanup(&it);
+ goto fail;
+ }
}
+fail:
*first_bad_bs = bs;
return ret;
}
-int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs)
+int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs,
+ Error **errp)
{
- int err = 0;
- BlockDriverState *bs = NULL;
+ int ret = 0;
+ BlockDriverState *bs;
+ BdrvNextIterator it;
- while (err == 0 && (bs = bdrv_next(bs))) {
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
if (bdrv_can_snapshot(bs)) {
- err = bdrv_snapshot_goto(bs, name);
+ ret = bdrv_snapshot_goto(bs, name, errp);
}
aio_context_release(ctx);
+ if (ret < 0) {
+ bdrv_next_cleanup(&it);
+ goto fail;
+ }
}
+fail:
*first_bad_bs = bs;
- return err;
+ return ret;
}
int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs)
{
QEMUSnapshotInfo sn;
int err = 0;
- BlockDriverState *bs = NULL;
+ BlockDriverState *bs;
+ BdrvNextIterator it;
- while (err == 0 && (bs = bdrv_next(bs))) {
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
err = bdrv_snapshot_find(bs, &sn, name);
}
aio_context_release(ctx);
+ if (err < 0) {
+ bdrv_next_cleanup(&it);
+ goto fail;
+ }
}
+fail:
*first_bad_bs = bs;
return err;
}
BlockDriverState **first_bad_bs)
{
int err = 0;
- BlockDriverState *bs = NULL;
+ BlockDriverState *bs;
+ BdrvNextIterator it;
- while (err == 0 && (bs = bdrv_next(bs))) {
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
err = bdrv_snapshot_create(bs, sn);
}
aio_context_release(ctx);
+ if (err < 0) {
+ bdrv_next_cleanup(&it);
+ goto fail;
+ }
}
+fail:
*first_bad_bs = bs;
return err;
}
BlockDriverState *bdrv_all_find_vmstate_bs(void)
{
- bool not_found = true;
- BlockDriverState *bs = NULL;
+ BlockDriverState *bs;
+ BdrvNextIterator it;
- while (not_found && (bs = bdrv_next(bs))) {
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *ctx = bdrv_get_aio_context(bs);
+ bool found;
aio_context_acquire(ctx);
- not_found = !bdrv_can_snapshot(bs);
+ found = bdrv_can_snapshot(bs);
aio_context_release(ctx);
+
+ if (found) {
+ bdrv_next_cleanup(&it);
+ break;
+ }
}
return bs;
}