Skip to content

Commit

Permalink
snapshot: distinguish id and name in snapshot delete
Browse files Browse the repository at this point in the history
Snapshot creation actually already distinguish id and name since it take
a structured parameter *sn, but delete can't. Later an accurate delete
is needed in qmp_transaction abort and blockdev-snapshot-delete-sync,
so change its prototype. Also *errp is added to tip error, but return
value is kepted to let caller check what kind of error happens. Existing
caller for it are savevm, delvm and qemu-img, they are not impacted by
introducing a new function bdrv_snapshot_delete_by_id_or_name(), which
check the return value and do the operation again.

Before this patch:
  For qcow2, it search id first then name to find the one to delete.
  For rbd, it search name.
  For sheepdog, it does nothing.

After this patch:
  For qcow2, logic is the same by call it twice in caller.
  For rbd, it always fails in delete with id, but still search for name
in second try, no change to user.

Some code for *errp is based on Pavel's patch.

Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
  • Loading branch information
Wenchao Xia authored and kevmw committed Sep 12, 2013
1 parent 2ea1dd7 commit a89d89d
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 43 deletions.
55 changes: 39 additions & 16 deletions block/qcow2-snapshot.c
Expand Up @@ -297,31 +297,47 @@ static void find_new_snapshot_id(BlockDriverState *bs,
snprintf(id_str, id_str_size, "%d", id_max + 1);
}

static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str)
static int find_snapshot_by_id_and_name(BlockDriverState *bs,
const char *id,
const char *name)
{
BDRVQcowState *s = bs->opaque;
int i;

for(i = 0; i < s->nb_snapshots; i++) {
if (!strcmp(s->snapshots[i].id_str, id_str))
return i;
if (id && name) {
for (i = 0; i < s->nb_snapshots; i++) {
if (!strcmp(s->snapshots[i].id_str, id) &&
!strcmp(s->snapshots[i].name, name)) {
return i;
}
}
} else if (id) {
for (i = 0; i < s->nb_snapshots; i++) {
if (!strcmp(s->snapshots[i].id_str, id)) {
return i;
}
}
} else if (name) {
for (i = 0; i < s->nb_snapshots; i++) {
if (!strcmp(s->snapshots[i].name, name)) {
return i;
}
}
}

return -1;
}

static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
static int find_snapshot_by_id_or_name(BlockDriverState *bs,
const char *id_or_name)
{
BDRVQcowState *s = bs->opaque;
int i, ret;
int ret;

ret = find_snapshot_by_id(bs, name);
if (ret >= 0)
ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL);
if (ret >= 0) {
return ret;
for(i = 0; i < s->nb_snapshots; i++) {
if (!strcmp(s->snapshots[i].name, name))
return i;
}
return -1;
return find_snapshot_by_id_and_name(bs, NULL, id_or_name);
}

/* if no id is provided, a new one is constructed */
Expand All @@ -343,7 +359,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
}

/* Check that the ID is unique */
if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) {
if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) {
return -EEXIST;
}

Expand Down Expand Up @@ -560,15 +576,19 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
return ret;
}

int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
int qcow2_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp)
{
BDRVQcowState *s = bs->opaque;
QCowSnapshot sn;
int snapshot_index, ret;

/* Search the snapshot */
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
if (snapshot_index < 0) {
error_setg(errp, "Can't find the snapshot");
return -ENOENT;
}
sn = s->snapshots[snapshot_index];
Expand All @@ -580,6 +600,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
s->nb_snapshots--;
ret = qcow2_write_snapshots(bs);
if (ret < 0) {
error_setg(errp, "Failed to remove snapshot from snapshot list");
return ret;
}

Expand All @@ -597,6 +618,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
sn.l1_size, -1);
if (ret < 0) {
error_setg(errp, "Failed to free the cluster and L1 table");
return ret;
}
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t),
Expand All @@ -605,6 +627,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
/* must update the copied flag on the current cluster offsets */
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
if (ret < 0) {
error_setg(errp, "Failed to update snapshot status in disk");
return ret;
}

Expand Down
5 changes: 4 additions & 1 deletion block/qcow2.h
Expand Up @@ -467,7 +467,10 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs);
/* qcow2-snapshot.c functions */
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
int qcow2_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp);
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);

Expand Down
21 changes: 20 additions & 1 deletion block/rbd.c
Expand Up @@ -891,12 +891,31 @@ static int qemu_rbd_snap_create(BlockDriverState *bs,
}

static int qemu_rbd_snap_remove(BlockDriverState *bs,
const char *snapshot_name)
const char *snapshot_id,
const char *snapshot_name,
Error **errp)
{
BDRVRBDState *s = bs->opaque;
int r;

if (!snapshot_name) {
error_setg(errp, "rbd need a valid snapshot name");
return -EINVAL;
}

/* If snapshot_id is specified, it must be equal to name, see
qemu_rbd_snap_list() */
if (snapshot_id && strcmp(snapshot_id, snapshot_name)) {
error_setg(errp,
"rbd do not support snapshot id, it should be NULL or "
"equal to snapshot name");
return -EINVAL;
}

r = rbd_snap_remove(s->image, snapshot_name);
if (r < 0) {
error_setg_errno(errp, -r, "Failed to remove the snapshot");
}
return r;
}

Expand Down
5 changes: 4 additions & 1 deletion block/sheepdog.c
Expand Up @@ -2072,7 +2072,10 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
return ret;
}

static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
static int sd_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp)
{
/* FIXME: Delete specified snapshot id. */
return 0;
Expand Down
58 changes: 55 additions & 3 deletions block/snapshot.c
Expand Up @@ -182,21 +182,73 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
return -ENOTSUP;
}

int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
/**
* Delete an internal snapshot by @snapshot_id and @name.
* @bs: block device used in the operation
* @snapshot_id: unique snapshot ID, or NULL
* @name: snapshot name, or NULL
* @errp: location to store error
*
* If both @snapshot_id and @name are specified, delete the first one with
* id @snapshot_id and name @name.
* If only @snapshot_id is specified, delete the first one with id
* @snapshot_id.
* If only @name is specified, delete the first one with name @name.
* if none is specified, return -ENINVAL.
*
* Returns: 0 on success, -errno on failure. If @bs is not inserted, return
* -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
* does not support internal snapshot deletion, return -ENOTSUP. If @bs does
* not support parameter @snapshot_id or @name, or one of them is not correctly
* specified, return -EINVAL. If @bs can't find one matching @id and @name,
* return -ENOENT. If @errp != NULL, it will always be filled with error
* message on failure.
*/
int bdrv_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp)
{
BlockDriver *drv = bs->drv;
if (!drv) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
return -ENOMEDIUM;
}
if (!snapshot_id && !name) {
error_setg(errp, "snapshot_id and name are both NULL");
return -EINVAL;
}
if (drv->bdrv_snapshot_delete) {
return drv->bdrv_snapshot_delete(bs, snapshot_id);
return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
}
if (bs->file) {
return bdrv_snapshot_delete(bs->file, snapshot_id);
return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp);
}
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
drv->format_name, bdrv_get_device_name(bs),
"internal snapshot deletion");
return -ENOTSUP;
}

void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
const char *id_or_name,
Error **errp)
{
int ret;
Error *local_err = NULL;

ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err);
if (ret == -ENOENT || ret == -EINVAL) {
error_free(local_err);
local_err = NULL;
ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err);
}

if (ret < 0) {
error_propagate(errp, local_err);
}
}

int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info)
{
Expand Down
5 changes: 4 additions & 1 deletion include/block/block_int.h
Expand Up @@ -150,7 +150,10 @@ struct BlockDriver {
QEMUSnapshotInfo *sn_info);
int (*bdrv_snapshot_goto)(BlockDriverState *bs,
const char *snapshot_id);
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
int (*bdrv_snapshot_delete)(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp);
int (*bdrv_snapshot_list)(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info);
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
Expand Down
8 changes: 7 additions & 1 deletion include/block/snapshot.h
Expand Up @@ -51,7 +51,13 @@ int bdrv_snapshot_create(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info);
int bdrv_snapshot_goto(BlockDriverState *bs,
const char *snapshot_id);
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
int bdrv_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp);
void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
const char *id_or_name,
Error **errp);
int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info);
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
Expand Down
11 changes: 7 additions & 4 deletions qemu-img.c
Expand Up @@ -2006,6 +2006,7 @@ static int img_snapshot(int argc, char **argv)
int action = 0;
qemu_timeval tv;
bool quiet = false;
Error *err = NULL;

bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR;
/* Parse commandline parameters */
Expand Down Expand Up @@ -2098,10 +2099,12 @@ static int img_snapshot(int argc, char **argv)
break;

case SNAPSHOT_DELETE:
ret = bdrv_snapshot_delete(bs, snapshot_name);
if (ret) {
error_report("Could not delete snapshot '%s': %d (%s)",
snapshot_name, ret, strerror(-ret));
bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err);
if (error_is_set(&err)) {
error_report("Could not delete snapshot '%s': (%s)",
snapshot_name, error_get_pretty(err));
error_free(err);
ret = 1;
}
break;
}
Expand Down
32 changes: 17 additions & 15 deletions savevm.c
Expand Up @@ -2325,18 +2325,21 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
{
BlockDriverState *bs;
QEMUSnapshotInfo sn1, *snapshot = &sn1;
int ret;
Error *err = NULL;

bs = NULL;
while ((bs = bdrv_next(bs))) {
if (bdrv_can_snapshot(bs) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0)
{
ret = bdrv_snapshot_delete(bs, name);
if (ret < 0) {
bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
if (error_is_set(&err)) {
monitor_printf(mon,
"Error while deleting snapshot on '%s'\n",
bdrv_get_device_name(bs));
"Error while deleting snapshot on device '%s':"
" %s\n",
bdrv_get_device_name(bs),
error_get_pretty(err));
error_free(err);
return -1;
}
}
Expand Down Expand Up @@ -2550,7 +2553,7 @@ int load_vmstate(const char *name)
void do_delvm(Monitor *mon, const QDict *qdict)
{
BlockDriverState *bs, *bs1;
int ret;
Error *err = NULL;
const char *name = qdict_get_str(qdict, "name");

bs = find_vmstate_bs();
Expand All @@ -2562,15 +2565,14 @@ void do_delvm(Monitor *mon, const QDict *qdict)
bs1 = NULL;
while ((bs1 = bdrv_next(bs1))) {
if (bdrv_can_snapshot(bs1)) {
ret = bdrv_snapshot_delete(bs1, name);
if (ret < 0) {
if (ret == -ENOTSUP)
monitor_printf(mon,
"Snapshots not supported on device '%s'\n",
bdrv_get_device_name(bs1));
else
monitor_printf(mon, "Error %d while deleting snapshot on "
"'%s'\n", ret, bdrv_get_device_name(bs1));
bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
if (error_is_set(&err)) {
monitor_printf(mon,
"Error while deleting snapshot on device '%s':"
" %s\n",
bdrv_get_device_name(bs),
error_get_pretty(err));
error_free(err);
}
}
}
Expand Down

0 comments on commit a89d89d

Please sign in to comment.