int ploop_di_remove_image(struct ploop_disk_images_data *di, const char *guid, int renew_top_uuid, char **fname) { int snap_id, image_id, nr_ch; struct ploop_image_data *image = NULL; struct ploop_snapshot_data *snapshot = NULL; snap_id = find_snapshot_by_guid(di, guid); if (snap_id == -1) { ploop_err(0, "Unable to find snapshot by uuid %s", guid); return SYSEXIT_PARAM; } snapshot = di->snapshots[snap_id]; image_id = find_image_idx_by_guid(di, guid); if (image_id == -1) { ploop_err(0, "Unable to find image by uuid %s", guid); return SYSEXIT_PARAM; } nr_ch = ploop_get_child_count_by_uuid(di, guid); if (nr_ch != 0) { ploop_err(0, "Unable to delete snapshot %s: " "it has %d child%s", guid, nr_ch, (nr_ch == 1) ? "" : "ren"); return SYSEXIT_PARAM; } if (guidcmp(snapshot->parent_guid, NONE_UUID) == 0) { ploop_err(0, "Unable to delete image %s: it is a base image", guid); return SYSEXIT_PARAM; } image = di->images[image_id]; if (fname != NULL) { *fname = strdup(image->file); if (*fname == NULL) return SYSEXIT_MALLOC; } ploop_log(3, "del snapshot %s", guid); // update top uuid if (renew_top_uuid && guidcmp(guid, di->top_guid) == 0) ploop_di_change_guid(di, snapshot->parent_guid, TOPDELTA_UUID); remove_data_from_array((void**)di->snapshots, di->nsnapshots, snap_id); di->nsnapshots--; remove_data_from_array((void**)di->images, di->nimages, image_id); di->nimages--; free_snapshot_data(snapshot); free_image_data(image); return 0; }
/* delete snapshot by guid * 1) if guid is not active and last -> delete guid * 2) if guid is not last merge with child -> delete child */ static int do_delete_snapshot(struct ploop_disk_images_data *di, const char *guid) { int ret; char conf[PATH_MAX]; int nelem = 0; char dev[64]; int snap_id; if (is_old_snapshot_format(di)) return SYSEXIT_PARAM; snap_id = find_snapshot_by_guid(di, guid); if (snap_id == -1) { ploop_err(0, "Can't find snapshot by uuid %s", guid); return SYSEXIT_NOSNAP; } ret = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (ret == -1) return SYSEXIT_SYS; else if (ret == 0 && strcmp(di->top_guid, guid) == 0) { ret = SYSEXIT_PARAM; ploop_err(0, "Unable to delete active snapshot %s", guid); return SYSEXIT_PARAM; } nelem = ploop_get_child_count_by_uuid(di, guid); if (nelem == 0) { struct ploop_snapshot_data *snap = di->snapshots[snap_id]; if (strcmp(snap->parent_guid, NONE_UUID) == 0) { ploop_err(0, "Unable to delete base image"); return SYSEXIT_PARAM; } if (strcmp(di->top_guid, guid) == 0) { int id = find_snapshot_by_guid(di, snap->parent_guid); if (id == -1) { ploop_err(0, "Can't find snapshot by uuid %s", snap->parent_guid); return SYSEXIT_PARAM; } if (di->snapshots[id]->temporary) { ploop_err(0, "Unable to delete top delta," " parent snapshot is temporary"); return SYSEXIT_PARAM; } } char *fname = find_image_by_guid(di, guid); if (fname == NULL) { ploop_err(0, "Unable to find image by uuid %s", guid); return SYSEXIT_PARAM; } ret = check_snapshot_mount(di, guid, fname, snap->temporary); if (ret) return ret; fname = NULL; /* snapshot is not active and last -> delete */ ret = ploop_di_remove_image(di, guid, 1, &fname); if (ret) return ret; get_disk_descriptor_fname(di, conf, sizeof(conf)); ret = ploop_store_diskdescriptor(conf, di); if (ret) { free(fname); return ret; } ploop_log(0, "Removing %s", fname); if (fname != NULL && unlink(fname)) { ploop_err(errno, "unlink %s", fname); free(fname); return SYSEXIT_UNLINK; } free(fname); if (ret == 0) ploop_log(0, "ploop snapshot %s has been successfully deleted", guid); } else if (nelem == 1) { const char *child_guid = ploop_find_child_by_guid(di, guid); if (child_guid == NULL) { ploop_err(0, "Can't find child of uuid %s", guid); return SYSEXIT_PARAM; } ret = ploop_merge_snapshot_by_guid(di, child_guid, NULL); } else if (!di->snapshots[snap_id]->temporary) { ploop_log(1, "Warning: Unable to delete snapshot %s as there are %d references" " to it; marking it as temporary instead", guid, nelem); di->snapshots[snap_id]->temporary = 1; get_disk_descriptor_fname(di, conf, sizeof(conf)); ret = ploop_store_diskdescriptor(conf, di); } return ret; }
int ploop_di_merge_image(struct ploop_disk_images_data *di, const char *guid, char **fname) { int i, snap_id, image_id, nr_ch; struct ploop_image_data *image = NULL; struct ploop_snapshot_data *snapshot = NULL; snap_id = find_snapshot_by_guid(di, guid); if (snap_id == -1) { ploop_err(0, "Unable to find snapshot by uuid %s", guid); return SYSEXIT_PARAM; } snapshot = di->snapshots[snap_id]; image_id = find_image_idx_by_guid(di, guid); if (image_id == -1) { ploop_err(0, "Unable to find image by uuid %s", guid); return SYSEXIT_PARAM; } nr_ch = ploop_get_child_count_by_uuid(di, snapshot->parent_guid); if (nr_ch > 1) { ploop_err(0, "Unable to merge snapshot %s: " "it has %d children", guid, nr_ch); return SYSEXIT_PARAM; } if (guidcmp(snapshot->parent_guid, NONE_UUID) == 0) { ploop_err(0, "Unable to merge image %s: it is a base image", guid); return SYSEXIT_PARAM; } image = di->images[image_id]; if (fname != NULL) { *fname = strdup(image->file); if (*fname == NULL) return SYSEXIT_MALLOC; } ploop_log(3, "merge snapshot %s -> %s", snapshot->guid, snapshot->parent_guid); /* Caller passed child_guid S2 to delete S1 (S1 <- S2 <- S3) (S2 <- S3) * so it has merge S2 to S1 and we should update all S1 referrences to S2 */ for (i = 0; i < di->nsnapshots; i++) { if (guidcmp(di->snapshots[i]->guid, snapshot->parent_guid) == 0) { strcpy(di->snapshots[i]->guid, guid); /* preserve temporary flag */ di->snapshots[i]->temporary = snapshot->temporary; } } for (i = 0; i < di->nimages; i++) if (guidcmp(di->images[i]->guid, snapshot->parent_guid) == 0) strcpy(di->images[i]->guid, guid); remove_data_from_array((void**)di->snapshots, di->nsnapshots, snap_id); di->nsnapshots--; remove_data_from_array((void**)di->images, di->nimages, image_id); di->nimages--; if (guidcmp(snapshot->guid, TOPDELTA_UUID) == 0) ploop_di_change_guid(di, snapshot->parent_guid, TOPDELTA_UUID); free_snapshot_data(snapshot); free_image_data(image); return 0; }