예제 #1
0
파일: xml.c 프로젝트: kolyshkin/ploop
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;
}
예제 #2
0
파일: di.c 프로젝트: grze/ploop
void ploop_di_set_temporary(struct ploop_disk_images_data *di, const char *guid)
{
	int i;

	i = find_snapshot_by_guid(di, guid);
	if (i != -1)
		di->snapshots[i]->temporary = 1;
}
예제 #3
0
파일: di.c 프로젝트: grze/ploop
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;
}
예제 #4
0
파일: di.c 프로젝트: grze/ploop
char *ploop_find_parent_by_guid(struct ploop_disk_images_data *di, const char *guid)
{
	int i;

	i = find_snapshot_by_guid(di, guid);
	if (i == -1)
		return NULL;
	if (guidcmp(di->snapshots[i]->parent_guid, NONE_UUID) == 0)
		return NULL;
	return di->snapshots[i]->parent_guid;
}
예제 #5
0
파일: xml.c 프로젝트: kolyshkin/ploop
/* Convert to new format with constant TopGUID */
static int convert_disk_descriptor(struct ploop_disk_images_data *di)
{
	if (di->top_guid == NULL) {
		ploop_err(0, "Invalid DiskDescriptor.xml: TopGUID not found");
		return -1;
	}
	if (!guidcmp(di->top_guid, TOPDELTA_UUID))
		return 0;

	ploop_log(0, "DiskDescriptor.xml is in old format: converting");

	if ((find_image_by_guid(di, TOPDELTA_UUID) != NULL) ||
			(find_snapshot_by_guid(di, TOPDELTA_UUID) != -1)) {
		ploop_err(0, "Can't convert: %s is in use",
				TOPDELTA_UUID);
		return -1;
	}

	ploop_log(0, "Changing %s to %s",
			di->top_guid, TOPDELTA_UUID);
	ploop_di_change_guid(di, di->top_guid, TOPDELTA_UUID);

	return 0;
}
예제 #6
0
파일: snapshot.c 프로젝트: kolyshkin/ploop
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;
}
예제 #7
0
파일: snapshot.c 프로젝트: kolyshkin/ploop
/* 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;
}
예제 #8
0
파일: snapshot.c 프로젝트: kolyshkin/ploop
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;
}
예제 #9
0
파일: di.c 프로젝트: grze/ploop
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;
}
예제 #10
0
파일: snapshot.c 프로젝트: OpenVZ/ploop
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;
}