int ploop_add_snapshot_entry(struct ploop_disk_images_data *di, const char *guid, const char *parent_guid, int temporary) { struct ploop_snapshot_data **tmp; struct ploop_snapshot_data *data; if (!is_valid_guid(guid)) { ploop_err(0, "ploop_add_snapshot_entry: invalid guid %s", guid); return SYSEXIT_PARAM; } if (!is_valid_guid(parent_guid)) { ploop_err(0, "ploop_add_snapshot_entry: invalid parent guid %s", parent_guid); return SYSEXIT_PARAM; } data = calloc(1, sizeof(struct ploop_snapshot_data)); if (data == NULL) { ploop_err(ENOMEM, "calloc failed"); return SYSEXIT_MALLOC; } tmp = realloc(di->snapshots, sizeof(struct ploop_snapshot_data *) * (di->nsnapshots+1)); if (tmp == NULL) { ploop_err(ENOMEM, "realloc failed"); free(data); return SYSEXIT_MALLOC; } di->snapshots = tmp; data->guid = strdup(guid); data->parent_guid = strdup(parent_guid); data->temporary = temporary; if (data->guid == NULL || data->parent_guid == NULL) { ploop_err(ENOMEM, "strdup failed"); free_snapshot_data(data); return SYSEXIT_MALLOC; } di->snapshots[di->nsnapshots] = data; di->nsnapshots++; return 0; }
static int validate_disk_descriptor(struct ploop_disk_images_data *di) { if (di->nimages == 0) { ploop_err(0, "No images found in %s", di->runtime->xml_fname); return -1; } if (!is_valid_blocksize(di->blocksize)) { ploop_err(0, "Invalid block size %d", di->blocksize); return -1; } // FIXME: compatibility issue have to be removed before BETA if (di->nimages != di->nsnapshots) { int ret; ret = ploop_add_snapshot_entry(di, TOPDELTA_UUID, NONE_UUID, 0); if (ret) return ret; if (di->top_guid == NULL) di->top_guid = strdup(TOPDELTA_UUID); } if (di->top_guid == NULL && find_snapshot_by_guid(di, TOPDELTA_UUID) != -1) { /* Parallels VM compatibility. * The top delta is hardcoded {5fbaabe3-6958-40ff-92a7-860e329aab41} */ di->top_guid = strdup(TOPDELTA_UUID); } if (!is_valid_guid(di->top_guid)) { ploop_err(0, "Validation of %s failed: invalid top delta %s", di->runtime->xml_fname, di->top_guid); return -1; } int i = find_snapshot_by_guid(di, di->top_guid); if (i == -1) { ploop_err(0, "Validation of %s failed: top delta %s is not found", di->runtime->xml_fname, di->top_guid); return -1; } if (di->snapshots[i]->temporary) { ploop_err(0, "Validation of %s failed: top delta %s is temporary", di->runtime->xml_fname, di->top_guid); return -1; } if (di->nimages != di->nsnapshots) { ploop_err(0, "Validation of %s failed: images(%d) != snapshots(%d)", di->runtime->xml_fname, di->nimages, di->nsnapshots); return -1; } return 0; }
int ploop_add_image_entry(struct ploop_disk_images_data *di, const char *fname, const char *guid) { struct ploop_image_data **tmp; struct ploop_image_data *image; if (!is_valid_guid(guid)) { ploop_err(0, "ploop_add_image_entry: invalid guid %s", guid); return SYSEXIT_PARAM; } image = calloc(1, sizeof(struct ploop_image_data)); if (image == NULL) { ploop_err(0, "calloc failed"); return SYSEXIT_MALLOC; } tmp = realloc(di->images, sizeof(struct ploop_image_data *) * (di->nimages+1)); if (tmp == NULL) { ploop_err(0, "realloc failed"); free(image); return SYSEXIT_MALLOC; } di->images = tmp; image->guid = strdup(guid); image->file = strdup(fname); if (image->guid == NULL || image->file == NULL) { ploop_err(ENOMEM, "strdup failed"); free_image_data(image); return SYSEXIT_MALLOC; } di->images[di->nimages] = image; di->nimages++; return 0; }
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; }
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; }
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; }