static int reset_top_delta(struct ploop_disk_images_data *di, struct ploop_snapshot_switch_param *param) { int ret; char *old_top_delta_fname = NULL; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; const char *guid = param->guid; ret = ploop_di_remove_image(di, di->top_guid, 0, &old_top_delta_fname); if (ret) return ret; ploop_di_change_guid(di, guid, TOPDELTA_UUID); get_disk_descriptor_fname(di, conf, sizeof(conf)); snprintf(conf_tmp, sizeof(conf_tmp), "%s.tmp", conf); ret = ploop_store_diskdescriptor(conf_tmp, di); if (ret) goto err; if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; goto err; } /* destroy precached info */ drop_statfs_info(di->images[0]->file); if (old_top_delta_fname != NULL) { ploop_log(0, "Removing %s", old_top_delta_fname); if (unlink(old_top_delta_fname)) ploop_err(errno, "Can't unlink %s", old_top_delta_fname); } err: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); free(old_top_delta_fname); return ret; }
int ploop_switch_snapshot_ex(struct ploop_disk_images_data *di, struct ploop_snapshot_switch_param *param) { int ret; int fd; char dev[64]; char uuid[UUID_SIZE]; char file_uuid[UUID_SIZE]; char new_top_delta_fname[PATH_MAX] = ""; char *old_top_delta_fname = NULL; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; off_t size; const char *guid = param->guid; int flags = param->flags; __u32 blocksize; int version; int snap_id; if (!is_valid_guid(guid)) { ploop_err(0, "Incorrect guid %s", guid); return SYSEXIT_PARAM; } if (ploop_lock_dd(di)) return SYSEXIT_LOCK; if (is_old_snapshot_format(di)) { ret = SYSEXIT_PARAM; goto err_cleanup1; } ret = SYSEXIT_PARAM; if (strcmp(di->top_guid, guid) == 0) { ploop_err(errno, "Nothing to do, already on %s snapshot", guid); goto err_cleanup1; } snap_id = find_snapshot_by_guid(di, guid); if (snap_id== -1) { ploop_err(0, "Can't find snapshot by uuid %s", guid); goto err_cleanup1; } if (di->snapshots[snap_id]->temporary) { ploop_err(0, "Snapshot %s is temporary", guid); goto err_cleanup1; } if (flags & PLOOP_SNAP_SKIP_TOPDELTA_CREATE) { ret = reset_top_delta(di, param); goto err_cleanup1; } // Read image param from snapshot we going to switch on ret = get_image_param(di, guid, &size, &blocksize, &version); if (ret) goto err_cleanup1; ret = gen_uuid_pair(uuid, sizeof(uuid), file_uuid, sizeof(file_uuid)); if (ret) { ploop_err(errno, "Can't generate uuid"); goto err_cleanup1; } if (!(flags & PLOOP_SNAP_SKIP_TOPDELTA_DESTROY)) { // device should be stopped ret = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (ret == -1) { ret = SYSEXIT_SYS; goto err_cleanup1; } else if (ret == 0) { ret = SYSEXIT_PARAM; ploop_err(0, "Unable to perform switch to snapshot operation" " on running device (%s)", dev); goto err_cleanup1; } ret = ploop_di_remove_image(di, di->top_guid, 0, &old_top_delta_fname); if (ret) goto err_cleanup1; } else if (param->guid_old != NULL) { if (!is_valid_guid(param->guid_old)) { ret = SYSEXIT_PARAM; ploop_err(0, "Incorrect guid %s", param->guid_old); goto err_cleanup1; } if (find_snapshot_by_guid(di, param->guid_old) != -1) { ret = SYSEXIT_PARAM; ploop_err(0, "Incorrect guid_old %s: already exists", param->guid_old); goto err_cleanup1; } ploop_di_change_guid(di, di->top_guid, param->guid_old); } snprintf(new_top_delta_fname, sizeof(new_top_delta_fname), "%s.%s", di->images[0]->file, file_uuid); ret = ploop_di_add_image(di, new_top_delta_fname, TOPDELTA_UUID, guid); if (ret) goto err_cleanup1; get_disk_descriptor_fname(di, conf, sizeof(conf)); snprintf(conf_tmp, sizeof(conf_tmp), "%s.tmp", conf); ret = ploop_store_diskdescriptor(conf_tmp, di); if (ret) goto err_cleanup1; // offline snapshot fd = create_snapshot_delta(new_top_delta_fname, blocksize, size, version); if (fd == -1) { ret = SYSEXIT_CREAT; goto err_cleanup2; } close(fd); if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; goto err_cleanup3; } /* destroy precached info */ drop_statfs_info(di->images[0]->file); if (old_top_delta_fname != NULL) { ploop_log(0, "Removing %s", old_top_delta_fname); if (unlink(old_top_delta_fname) && errno != ENOENT) ploop_err(errno, "Can't unlink %s", old_top_delta_fname); } ploop_log(0, "ploop snapshot has been successfully switched"); err_cleanup3: if (ret && unlink(new_top_delta_fname)) ploop_err(errno, "Can't unlink %s", conf_tmp); err_cleanup2: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); err_cleanup1: ploop_unlock_dd(di); free(old_top_delta_fname); return ret; }
/* 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; }
static int do_create_snapshot(struct ploop_disk_images_data *di, const char *guid, const char *snap_dir, int temporary) { int ret; int fd; char dev[64]; char snap_guid[UUID_SIZE]; char file_guid[UUID_SIZE]; char fname[PATH_MAX]; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; int online = 0; int n; off_t size; __u32 blocksize; int version; if (guid != NULL && !is_valid_guid(guid)) { ploop_err(0, "Incorrect guid %s", guid); return SYSEXIT_PARAM; } if (is_old_snapshot_format(di)) return SYSEXIT_PARAM; ret = gen_uuid_pair(snap_guid, sizeof(snap_guid), file_guid, sizeof(file_guid)); if (ret) { ploop_err(errno, "Can't generate uuid"); return ret; } if (guid != NULL) { if (find_snapshot_by_guid(di, guid) != -1) { ploop_err(0, "The snapshot %s already exist", guid); return SYSEXIT_PARAM; } strcpy(snap_guid, guid); } n = get_snapshot_count(di); if (n == -1) { return SYSEXIT_PARAM; } else if (n > 128-2) { /* The number of images limited by 128 so the snapshot limit 128 - base_image - one_reserverd */ ploop_err(errno, "Unable to create a snapshot." " The maximum number of snapshots (%d) has been reached", n-1); return SYSEXIT_PARAM; } ret = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (ret == -1) return SYSEXIT_SYS; else if (ret == 0) { online = 1; ret = complete_running_operation(di, dev); if (ret) return ret; } else { ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); if (ret == SYSEXIT_OPEN && errno == EBUSY) { /* repair top delta */ char *topdelta[] = {find_image_by_guid(di, di->top_guid), NULL}; blocksize = di->blocksize; ret = check_deltas(di, topdelta, 0, &blocksize); if (ret) return ret; ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); } if (ret) return ret; } ret = merge_temporary_snapshots(di); if (ret) return ret; if (snap_dir != NULL) { char *name; char *dir; dir = realpath(snap_dir, NULL); if (dir == NULL) { ploop_err(errno, "Error in realpath(%s)", snap_dir); return SYSEXIT_CREAT; } name = strrchr(di->images[0]->file, '/'); if (name != NULL) name++; else name = di->images[0]->file; snprintf(fname, sizeof(fname), "%s/%s.%s", dir, name, file_guid); free(dir); } else snprintf(fname, sizeof(fname), "%s.%s", di->images[0]->file, file_guid); ploop_di_change_guid(di, di->top_guid, snap_guid); if (temporary) ploop_di_set_temporary(di, snap_guid); ret = ploop_di_add_image(di, fname, TOPDELTA_UUID, snap_guid); if (ret) return ret; get_disk_descriptor_fname(di, conf, sizeof(conf)); snprintf(conf_tmp, sizeof(conf_tmp), "%s.tmp", conf); ret = ploop_store_diskdescriptor(conf_tmp, di); if (ret) return ret; if (!online) { // offline snapshot fd = create_snapshot_delta(fname, blocksize, size, version); if (fd < 0) { ret = SYSEXIT_CREAT; goto err; } close(fd); } else { // Always sync fs ret = create_snapshot(dev, fname, 1); if (ret) goto err; } if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; } if (ret && !online && unlink(fname)) ploop_err(errno, "Can't unlink %s", fname); ploop_log(0, "ploop %s %s has been successfully created", get_snap_str(temporary), snap_guid); err: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); return ret; }
void get_disk_descriptor_lock_fname(struct ploop_disk_images_data *di, char *out, int size) { get_disk_descriptor_fname(di, out, size); strcat(out, ".lck"); }
int do_create_snapshot(struct ploop_disk_images_data *di, const char *guid, const char *snap_dir, const char *cbt_uuid, int flags) { int ret, rc; int fd; char dev[64]; char snap_guid[UUID_SIZE]; char top_guid[UUID_SIZE]; char file_guid[UUID_SIZE]; char fname[PATH_MAX]; const char *prev_fname = NULL; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; int online = 0; int temporary = flags & SNAP_TYPE_TEMPORARY; int n; off_t size; __u32 blocksize; int version; uuid_t u; const __u8 *cbt_u = NULL; if (cbt_uuid != NULL) { ploop_log(0, "Create snapshot CBT uuid=%s", cbt_uuid); if (uuid_parse(cbt_uuid, u)) { ploop_log(-1, "Incorrect cbt uuid is specified %s", cbt_uuid); return SYSEXIT_PARAM; } cbt_u = u; } if (guid != NULL && !is_valid_guid(guid)) { ploop_err(0, "Incorrect guid %s", guid); return SYSEXIT_PARAM; } if (is_old_snapshot_format(di)) return SYSEXIT_PARAM; ret = gen_uuid_pair(snap_guid, sizeof(snap_guid), file_guid, sizeof(file_guid)); if (ret) return ret; if (di->vol && di->vol->parent) { ret = ploop_uuid_generate(top_guid, sizeof(top_guid)); if (ret) return ret; } else strcpy(top_guid, TOPDELTA_UUID); if (guid != NULL) { if (find_snapshot_by_guid(di, guid) != -1) { ploop_err(0, "The snapshot %s already exist", guid); return SYSEXIT_PARAM; } strcpy(snap_guid, guid); } n = get_snapshot_count(di); if (n == -1) { return SYSEXIT_PARAM; } else if (n > 128-2) { /* The number of images limited by 128 so the snapshot limit 128 - base_image - one_reserverd */ ploop_err(errno, "Unable to create a snapshot." " The maximum number of snapshots (%d) has been reached", n-1); return SYSEXIT_PARAM; } rc = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (rc == -1) return SYSEXIT_SYS; if (rc == 0) { if (flags & SNAP_TYPE_OFFLINE) { ret = get_image_param_online(dev, &size, &blocksize, &version); } else { online = 1; ret = complete_running_operation(di, dev); } if (ret) return ret; } else { ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); if (ret == SYSEXIT_OPEN && errno == EBUSY) { /* repair top delta */ char *topdelta[] = {find_image_by_guid(di, di->top_guid), NULL}; blocksize = di->blocksize; ret = check_deltas(di, topdelta, 0, &blocksize, NULL); if (ret) return ret; ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); } if (ret) return ret; } ret = merge_temporary_snapshots(di); if (ret) return ret; if (snap_dir != NULL) { char *name; char *dir; dir = realpath(snap_dir, NULL); if (dir == NULL) { ploop_err(errno, "Error in realpath(%s)", snap_dir); return SYSEXIT_CREAT; } name = strrchr(di->images[0]->file, '/'); if (name != NULL) name++; else name = di->images[0]->file; snprintf(fname, sizeof(fname), "%s/%s.%s", dir, name, file_guid); free(dir); } else snprintf(fname, sizeof(fname), "%s.%s", di->images[0]->file, file_guid); prev_fname = find_image_by_guid(di, di->top_guid); if (prev_fname == NULL) { ploop_err(0, "Unable to find image by uuid %s", di->top_guid); return SYSEXIT_PARAM; } ploop_di_change_guid(di, di->top_guid, snap_guid); if (temporary) ploop_di_set_temporary(di, snap_guid); ret = ploop_di_add_image(di, fname, top_guid, snap_guid); if (ret) return ret; get_disk_descriptor_fname(di, conf, sizeof(conf)); snprintf(conf_tmp, sizeof(conf_tmp), "%s.tmp", conf); ret = ploop_store_diskdescriptor(conf_tmp, di); if (ret) return ret; if (!online) { // offline snapshot ret = 0; fd = create_snapshot_delta(fname, blocksize, size, version); if (fd < 0) { ret = SYSEXIT_CREAT; goto err; } close(fd); if (cbt_u != NULL) ret = write_empty_cbt_to_image(fname, prev_fname, cbt_u); else if (di->mode != PLOOP_RAW_MODE) { if (rc == 0) ret = cbt_dump(di, dev, fname); else ret = ploop_move_cbt(fname, prev_fname); } if (ret) goto err; } else { // Always sync fs ret = create_snapshot(dev, fname, 1, cbt_u, prev_fname); if (ret) goto err; } if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; } if (ret && !online && unlink(fname)) ploop_err(errno, "Can't unlink %s", fname); ploop_log(0, "ploop %s %s has been successfully created", get_snap_str(temporary), snap_guid); err: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); return ret; }