@@ -423,6 +423,7 @@ void qcow2_snapshot_create(BlockDriverState *bs,
int i, ret;
uint64_t *l1_table = NULL;
int64_t l1_table_offset;
+ Error *err_rollback = NULL;
memset(sn, 0, sizeof(*sn));
@@ -471,7 +472,7 @@ void qcow2_snapshot_create(BlockDriverState *bs,
PRIu64 " with size %" PRIu64,
sn->l1_table_offset,
(uint64_t)(s->l1_size * sizeof(uint64_t)));
- goto fail;
+ goto dealloc_l1_table;
}
ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
@@ -482,7 +483,7 @@ void qcow2_snapshot_create(BlockDriverState *bs,
PRIu64 " with size %" PRIu64,
sn->l1_table_offset,
(uint64_t)(s->l1_size * sizeof(uint64_t)));
- goto fail;
+ goto dealloc_l1_table;
}
g_free(l1_table);
@@ -499,7 +500,7 @@ void qcow2_snapshot_create(BlockDriverState *bs,
"Failed in update of refcount for snapshot at %"
PRIu64 " with size %d",
s->l1_table_offset, s->l1_size);
- goto fail;
+ goto dealloc_l1_table;
}
/* Append the new snapshot to the snapshot list */
@@ -512,12 +513,18 @@ void qcow2_snapshot_create(BlockDriverState *bs,
s->snapshots = new_snapshot_list;
s->snapshots[s->nb_snapshots++] = *sn;
- ret = qcow2_write_snapshots(bs, errp, NULL);
+ ret = qcow2_write_snapshots(bs, errp, &err_rollback);
if (ret < 0) {
g_free(s->snapshots);
s->snapshots = old_snapshot_list;
s->nb_snapshots--;
- goto fail;
+ if (error_is_set(&err_rollback)) {
+ error_append(errp, "%s", error_get_pretty(err_rollback));
+ error_free(err_rollback);
+ goto fail;
+ } else {
+ goto restore_refcount;
+ }
}
g_free(old_snapshot_list);
@@ -537,6 +544,26 @@ void qcow2_snapshot_create(BlockDriverState *bs,
#endif
return;
+restore_refcount:
+ if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1)
+ < 0) {
+ /* Nothing can be done now, need image check later, skip following
+ rollback action. */
+ error_append(errp,
+ "Failed to restore refcounts during rollback");
+ goto fail;
+ }
+
+dealloc_l1_table:
+ ret = qcow2_free_clusters(bs, sn->l1_table_offset,
+ sn->l1_size * sizeof(uint64_t),
+ QCOW2_DISCARD_ALWAYS);
+ if (ret < 0) {
+ error_append(errp,
+ "Failed to free the L1 table during rollback: %s",
+ strerror(-ret));
+ }
+
fail:
g_free(sn->id_str);
g_free(sn->name);