예제 #1
0
파일: gpt.c 프로젝트: varung/ploop
int resize_gpt_partition(const char *devname, __u64 new_size)
{
	unsigned char buf[SECTOR_SIZE*GPT_DATA_SIZE]; // LBA1 header, LBA2-34 partition entry
	int fd;
	int part, ret;
	struct GptHeader *pt;
	struct GptEntry *pe;
	__u32 pt_crc32, pe_crc32, orig_crc;
	off_t size;
	__u64 tmp;

	ret = has_partition(devname, &part);
	if (ret)
		return ret;

	if (!part)
		return 0;

	ret = ploop_get_size(devname, &size);
	if (ret)
		return ret;

	// Resize up to max available space
	if (new_size == 0)
		new_size = size;

	if (new_size > size) {
		ploop_err(0, "Unable to resize GPT partition:"
				" incorrect parameter new_size=%llu size=%lu",
				new_size, (long)size);
		return SYSEXIT_PARAM;
	}

	ploop_log(1, "Resizing GPT partition to %ld", (long)new_size);
	fd = open(devname, O_RDWR);
	if (fd == -1) {
		ploop_err(errno, "open %s", devname);
		return SYSEXIT_OPEN;
	}
	// skip LBA0 Protective MBR
	ret = pread(fd, buf, sizeof(buf), SECTOR_SIZE);
	if (ret == -1) {
		ploop_err(errno, "pread %s", devname);
		goto err;
	}
	pt = (struct GptHeader *)buf;
	pe = (struct GptEntry *)(&buf[SECTOR_SIZE * GPT_HEADER_SIZE]);

	// Validate crc
	orig_crc = pt->header_crc32;
	pt->header_crc32 = 0;
	pt_crc32 = ploop_crc32((unsigned char *)pt, pt->header_size);
	if (pt_crc32 != orig_crc) {
		ploop_err(0, "GPT validation failed orig crc %x != %x",
				orig_crc, pt_crc32);
		ret = -1;
		goto err;
	}
	// change GPT header
	pt->alternate_lba = new_size - 1;
	pt->last_usable_lba = new_size - GPT_DATA_SIZE - 1;
	pe->ending_lba = (pt->last_usable_lba >> 3 << 3) - 1;

	// Recalculate crc32
	pe_crc32 = ploop_crc32((unsigned char *)pe, SECTOR_SIZE * GPT_PT_ENTRY_SIZE);
	pt->partition_entry_array_crc32 = pe_crc32;

	pt->header_crc32 = 0;
	pt_crc32 = ploop_crc32((unsigned char *)pt, pt->header_size);
	pt->header_crc32 = pt_crc32;

	ploop_log(0, "Storing GPT");
	ret = pwrite(fd, pt, SECTOR_SIZE * GPT_DATA_SIZE, SECTOR_SIZE);
	if (ret == -1) {
		ploop_err(errno, "Failed to store primary GPT %s", devname);
		goto err;
	}
	ret = fsync(fd);
	if (ret) {
		ploop_err(errno, "Can't fsync %s", devname);
		ret = SYSEXIT_FSYNC;
		goto err;
	}

	// Store secondary GPT entries
	tmp = pt->my_lba;
	pt->my_lba = pt->alternate_lba;
	pt->alternate_lba = tmp;
	pt->partition_entry_lba = pt->last_usable_lba + 1;

	// Recalculate crc32
	pt->header_crc32 = 0;
	pt_crc32 = ploop_crc32((unsigned char *)pt, pt->header_size);
	pt->header_crc32 = pt_crc32;

	ret = pwrite(fd, pe, SECTOR_SIZE * GPT_PT_ENTRY_SIZE,
			(new_size - GPT_DATA_SIZE)*SECTOR_SIZE);
	if (ret == -1) {
		ploop_err(errno, "Failed to store secondary GPT %s", devname);
		goto err;
	}

	// Store Secondary GPT header
	ret = pwrite(fd, pt, SECTOR_SIZE, (new_size - GPT_HEADER_SIZE)*SECTOR_SIZE);
	if (ret == -1) {
		ploop_err(errno, "Failed to store secondary GPT header %s", devname);
		goto err;
	}
	if (fsync(fd)) {
		ploop_err(errno, "Can't fsync %s", devname);
		ret = SYSEXIT_FSYNC;
		goto err;
	}

	blkpg_resize_partition(fd, pe);
	ret = 0;
err:
	close(fd);
	if (ret < 0)
		ret = SYSEXIT_CHANGE_GPT;
	return ret;
}
예제 #2
0
파일: pcopy.c 프로젝트: avagin/ploop
int ploop_copy_receive(struct ploop_copy_receive_param *arg)
{
	int ofd, ret;
	__u64 cluster = 0;
	void *iobuf = NULL;

	if (!arg)
		return SYSEXIT_PARAM;

	if (is_fd_pipe(arg->ifd) != 1) {
		ploop_err(errno, "Invalid input fd %d: must be "
				"a pipe or a socket", arg->ifd);
		return SYSEXIT_PARAM;
	}

	if (arg->feedback_fd >= 0 && is_fd_pipe(arg->feedback_fd) != 1) {
		ploop_err(errno, "Invalid feedback fd %d: must be "
				"a pipe or a socket", arg->feedback_fd);
		return SYSEXIT_PARAM;
	}

	/* If feedback is to be send to stdout or stderr,
	 * we have to disable logging to appropriate fd.
	 *
	 * As currently there's no way to disable just stderr,
	 * so in this case we have to disable stdout as well.
	 */
	if (arg->feedback_fd == STDOUT_FILENO)
		ploop_set_verbose_level(PLOOP_LOG_NOSTDOUT);
	else if (arg->feedback_fd == STDERR_FILENO)
		ploop_set_verbose_level(PLOOP_LOG_NOCONSOLE);

	ofd = open(arg->file, O_WRONLY|O_CREAT|O_EXCL, 0600);
	if (ofd < 0) {
		ploop_err(errno, "Can't open %s", arg->file);
		return SYSEXIT_CREAT;
	}

	/* Read data */
	for (;;) {
		int n;
		struct xfer_desc desc;

		if (nread(arg->ifd, &desc, sizeof(desc)) < 0) {
			ploop_err(errno, "Error in nread(desc)");
			ret = SYSEXIT_READ;
			goto out;
		}
		if (desc.marker != PLOOPCOPY_MARKER) {
			ploop_err(0, "Stream corrupted");
			ret = SYSEXIT_PROTOCOL;
			goto out;
		}
		if (desc.size > cluster) {
			free(iobuf);
			iobuf = NULL;
			cluster = desc.size;
			if (p_memalign(&iobuf, 4096, cluster)) {
				ret = SYSEXIT_MALLOC;
				goto out;
			}
		}
		if (desc.size == 0)
			break;

		if (nread(arg->ifd, iobuf, desc.size)) {
			ploop_err(errno, "Error in nread data");
			ret = SYSEXIT_READ;
			goto out;
		}
		if (desc.size == SYNC_MARK) {
			int st;
			/* ignore received data, instead do sync */
			st = fdatasync(ofd);
			if (arg->feedback_fd >= 0) {
				/* Tell the sending side how it went */
				int w;

				w = write(arg->feedback_fd,
					st ? STATUS_FAIL : STATUS_OK,
					LEN_STATUS);
				/* check write error only if no error yet */
				if (!st && w != LEN_STATUS) {
					ploop_err(errno, "Error in write(%d)",
							arg->feedback_fd);
					ret = SYSEXIT_WRITE;
					goto out;
				}
			}
			if (st) {
				ploop_err(errno, "Error in fdatasync()");
				ret = SYSEXIT_WRITE;
				goto out;
			}
			continue;
		}
		n = pwrite(ofd, iobuf, desc.size, desc.pos);
		if (n != desc.size) {
			if (n < 0)
				ploop_err(errno, "Error in pwrite");
			else
				ploop_err(0, "Error: short pwrite");
			ret = SYSEXIT_WRITE;
			goto out;
		}
	}

	if (fdatasync(ofd)) {
		ploop_err(errno, "Error in fdatasync");
		ret = SYSEXIT_WRITE;
		goto out;
	}

	ret = 0;

out:
	if (close(ofd)) {
		ploop_err(errno, "Error in close");
		if (!ret)
			ret = SYSEXIT_WRITE;
	}
	if (ret)
		unlink(arg->file);
	free(iobuf);

	return ret;
}
예제 #3
0
파일: pcopy.c 프로젝트: avagin/ploop
int ploop_copy_send(struct ploop_copy_send_param *arg)
{
	struct delta idelta = { .fd = -1 };
	int tracker_on = 0;
	int fs_frozen = 0;
	int devfd = -1;
	int mntfd = -1;
	int ret = 0;
	char *send_from = NULL;
	char *format = NULL;
	void *iobuf[2] = {};
	int blocksize;
	__u64 cluster;
	__u64 pos;
	__u64 iterpos;
	__u64 trackpos;
	__u64 trackend;
	__u64 xferred;
	int iter;
	struct ploop_track_extent e;
	int i;
	pthread_t send_th = 0;
	struct send_data sd = {
		.mutex = PTHREAD_MUTEX_INITIALIZER,
		.cond = PTHREAD_COND_INITIALIZER,
		.cond_sent = PTHREAD_COND_INITIALIZER,
	};

	if (!arg)
		return SYSEXIT_PARAM;

	sd.fd = arg->ofd;
	sd.is_pipe = is_fd_pipe(arg->ofd);
	if (sd.is_pipe < 0) {
		ploop_err(0, "Invalid output fd %d: must be a file, "
				"a pipe or a socket", arg->ofd);
		return SYSEXIT_PARAM;
	}

	if (arg->feedback_fd >= 0 && is_fd_pipe(arg->feedback_fd) != 1) {
		ploop_err(errno, "Invalid feedback fd %d: must be "
				"a pipe or a socket", arg->feedback_fd);
		return SYSEXIT_PARAM;
	}

	/* If data is to be send to stdout or stderr,
	 * we have to disable logging to appropriate fd.
	 *
	 * As currently there's no way to disable just stderr,
	 * so in this case we have to disable stdout as well.
	 */
	if (arg->ofd == STDOUT_FILENO)
		ploop_set_verbose_level(PLOOP_LOG_NOSTDOUT);
	else if (arg->ofd == STDERR_FILENO)
		ploop_set_verbose_level(PLOOP_LOG_NOCONSOLE);

	devfd = open(arg->device, O_RDONLY);
	if (devfd < 0) {
		ploop_err(errno, "Can't open device %s", arg->device);
		ret = SYSEXIT_DEVICE;
		goto done;
	}

	mntfd = open_mount_point(arg->device);
	if (mntfd < 0) {
		/* Error is printed by open_mount_point() */
		ret = SYSEXIT_OPEN;
		goto done;
	}

	ret = get_image_info(arg->device, &send_from, &format, &blocksize);
	if (ret)
		goto done;
	cluster = S2B(blocksize);

	ret = SYSEXIT_MALLOC;
	for (i = 0; i < 2; i++)
		if (p_memalign(&iobuf[i], 4096, cluster))
			goto done;

	ret = complete_running_operation(NULL, arg->device);
	if (ret)
		goto done;

	ret = ioctl_device(devfd, PLOOP_IOC_TRACK_INIT, &e);
	if (ret)
		goto done;
	tracker_on = 1;

	if (open_delta_simple(&idelta, send_from, O_RDONLY|O_DIRECT, OD_NOFLAGS)) {
		ret = SYSEXIT_OPEN;
		goto done;
	}

	ret = pthread_create(&send_th, NULL, send_thread, &sd);
	if (ret) {
		ploop_err(ret, "Can't create send thread");
		ret = SYSEXIT_SYS;
		goto done;
	}

	ploop_log(-1, "Sending %s", send_from);

	trackend = e.end;
	for (pos = 0; pos < trackend; ) {
		int n;

		trackpos = pos + cluster;
		ret = ioctl_device(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos);
		if (ret)
			goto done;

		n = do_pread(cluster, pos);
		if (n == 0) /* EOF */
			break;

		async_send(n, pos);
		pos += n;
	}
	/* First copy done */

	iter = 1;
	iterpos = 0;
	xferred = 0;

	for (;;) {
		int err;

		err = ioctl(devfd, PLOOP_IOC_TRACK_READ, &e);
		if (err == 0) {

			//fprintf(stderr, "TRACK %llu-%llu\n", e.start, e.end); fflush(stdout);

			if (e.end > trackend)
				trackend = e.end;

			if (e.start < iterpos)
				iter++;
			iterpos = e.end;
			xferred += e.end - e.start;

			for (pos = e.start; pos < e.end; ) {
				int n;
				int copy = e.end - pos;

				if (copy > cluster)
					copy = cluster;
				if (pos + copy > trackpos) {
					trackpos = pos + copy;
					if (ioctl(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos)) {
						ploop_err(errno, "PLOOP_IOC_TRACK_SETPOS");
						ret = SYSEXIT_DEVIOC;
						goto done;
					}
				}
				n = do_pread(copy, pos);
				if (n == 0) {
					ploop_err(0, "Unexpected EOF");
					ret = SYSEXIT_READ;
					goto done;
				}
				async_send(n, pos);
				pos += n;
			}
		} else {
			if (errno == EAGAIN) /* no more dirty blocks */
				break;
			ploop_err(errno, "PLOOP_IOC_TRACK_READ");
			ret = SYSEXIT_DEVIOC;
			goto done;
		}

		if (iter > 10 || (iter > 1 && xferred > trackend))
			break;
	}

	/* Live iterative transfers are done. Either we transferred
	 * everything or iterations did not converge. In any case
	 * now we must suspend VE disk activity. Now it is just
	 * call of an external program (something sort of
	 * "killall -9 writetest; sleep 1; umount /mnt2"), actual
	 * implementation must be intergrated to vzctl/vzmigrate
	 * and suspend VE with subsequent fsyncing FS.
	 */

	/* Send the sync command to receiving side. Since older ploop
	 * might be present on the other side, we need to not break the
	 * backward compatibility, so just send the first few (SYNC_MARK)
	 * bytes of delta file contents. New ploop_receive() interprets
	 * this as "sync me" command, while the old one just writes those
	 * bytes which is useless but harmless.
	 */
	if (sd.is_pipe) {
		char buf[LEN_STATUS + 1] = {};

		ret = do_pread(4096, 0);
		if (ret < SYNC_MARK) {
			ploop_err(errno, "Short read");
			ret = SYSEXIT_READ;
			goto done;
		}
		TS("SEND 0 %d (sync)", SYNC_MARK);
		async_send(SYNC_MARK, 0);

		/* Now we should wait for the other side to finish syncing
		 * before freezing the container, to optimize CT frozen time.
		 */
		if (arg->feedback_fd < 0) {
			/* No descriptor to receive a response back is given.
			 * As ugly as it looks, let's just sleep for some time
			 * hoping the other side will finish sync.
			 */
			TS("SLEEP 5");
			sleep(5);
			goto sync_done;
		}

		/* Wait for feedback from the receiving side */

		/* FIXME: use select/poll with a timeout */
		if (read(arg->feedback_fd, buf, LEN_STATUS)
				!= LEN_STATUS) {
			ploop_err(errno, "Can't read feedback");
			ret = SYSEXIT_PROTOCOL;
			goto done;
		}

		if (strncmp(buf, STATUS_OK, LEN_STATUS) == 0) {
			goto sync_done;
		}
		else if (strncmp(buf, STATUS_FAIL, LEN_STATUS) == 0) {
			ploop_err(0, "Remote side reported sync failure");
			ret = SYSEXIT_FSYNC;
			goto done;
		}
		else {
			ploop_err(0, "Got back feedback: %s", buf);
			ret = SYSEXIT_PROTOCOL;
			goto done;
		}
	} else {
		/* Writing to local file */
		fdatasync(arg->ofd);
	}

sync_done:
	/* Freeze the container */
	TS("FLUSH");
	ret = run_cmd(arg->flush_cmd);
	if (ret)
		goto done;

	/* Sync fs */
	TS("SYNCFS");
	if (sys_syncfs(mntfd)) {
		ploop_err(errno, "syncfs() failed");
		ret = SYSEXIT_FSYNC;
		goto done;
	}

	/* Flush journal and freeze fs (this also clears the fs dirty bit) */
	TS("FIFREEZE");
	ret = ioctl_device(mntfd, FIFREEZE, 0);
	if (ret)
		goto done;
	fs_frozen = 1;

	TS("IOC_SYNC");
	ret = ioctl_device(devfd, PLOOP_IOC_SYNC, 0);
	if (ret)
		goto done;

	iter = 1;
	iterpos = 0;

	for (;;) {
		int err;
		struct ploop_track_extent e;

		err = ioctl(devfd, PLOOP_IOC_TRACK_READ, &e);
		if (err == 0) {
			__u64 pos;

			//fprintf(stderr, "TRACK %llu-%llu\n", e.start, e.end); fflush(stdout);

			if (e.end > trackend)
				trackend = e.end;
			if (e.start < iterpos)
				iter++;
			iterpos = e.end;

			for (pos = e.start; pos < e.end; ) {
				int n;
				int copy = e.end - pos;

				if (copy > cluster)
					copy = cluster;
				if (pos + copy > trackpos) {
					trackpos = pos + copy;
					ret = ioctl(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos);
					if (ret)
						goto done;
				}
				TS("READ %llu %d", pos, copy);
				n = do_pread(copy, pos);
				if (n == 0) {
					ploop_err(0, "Unexpected EOF");
					ret = SYSEXIT_READ;
					goto done;
				}
				TS("SEND %llu %d", pos, n);
				async_send(n, pos);
				pos += n;
			}
		} else {
			if (errno == EAGAIN)
				break;

			ploop_err(errno, "PLOOP_IOC_TRACK_READ");
			ret = SYSEXIT_DEVIOC;
			goto done;
		}

		if (iter > 2) {
			ploop_err(0, "Too many iterations on frozen FS, aborting");
			ret = SYSEXIT_LOOP;
			goto done;
		}
	}

	/* Must clear dirty flag on ploop1 image. */
	if (strcmp(format, "ploop1") == 0) {
		int n;
		struct ploop_pvd_header *vh;

		TS("READ 0 4096");
		n = do_pread(4096, 0);
		if (n < SECTOR_SIZE) {
			ploop_err(errno, "Short read");
			ret = SYSEXIT_READ;
			goto done;
		}

		vh = iobuf[i];
		vh->m_DiskInUse = 0;

		TS("SEND 0 %d (1st sector)", SECTOR_SIZE);
		async_send(SECTOR_SIZE, 0);
	}

	TS("IOCTL TRACK_STOP");
	ret = ioctl(devfd, PLOOP_IOC_TRACK_STOP, 0);
	if (ret)
		goto done;
	tracker_on = 0;

	TS("SEND 0 0 (close)");
	async_send(0, 0);
	pthread_join(send_th, NULL);
	send_th = 0;

done:
	if (send_th)
		pthread_cancel(send_th);
	if (fs_frozen)
		(void)ioctl_device(mntfd, FITHAW, 0);
	if (tracker_on)
		(void)ioctl_device(devfd, PLOOP_IOC_TRACK_ABORT, 0);
	free(iobuf[0]);
	free(iobuf[1]);
	if (devfd >=0)
		close(devfd);
	if (mntfd >=0)
		close(mntfd);
	free(send_from);
	if (idelta.fd >= 0)
		close_delta(&idelta);

	TS("DONE");
	return ret;
}
#undef do_pread
#undef async_send

/* Deprecated, please use ploop_copy_send() instead */
int ploop_send(const char *device, int ofd, const char *flush_cmd,
		int is_pipe)
{
	struct ploop_copy_send_param s = {
		.device		= device,
		.ofd		= ofd,
		.flush_cmd	= flush_cmd,
	};

	return ploop_copy_send(&s);
}
예제 #4
0
파일: snapshot.c 프로젝트: OpenVZ/ploop
int ploop_create_temporary_snapshot(struct ploop_disk_images_data *di,
		struct ploop_tsnapshot_param *param, int *holder_fd)
{
	int ret;
	struct ploop_mount_param mount_param = { .ro = 1, };
	char component_name[PLOOP_COOKIE_SIZE];

	if (di == NULL || param == NULL)
		return SYSEXIT_PARAM;

	if (param->guid == NULL) {
		ploop_err(0, "Snapshot guid is not specified");
		return SYSEXIT_PARAM;
	}

	if (param->component_name == NULL) {
		ploop_err(0, "Component name is not specified");
		return SYSEXIT_PARAM;
	}

	if (ploop_lock_dd(di))
		return SYSEXIT_LOCK;

	ret = do_create_snapshot(di, param->guid, param->snap_dir,
			param->cbt_uuid, SNAP_TYPE_TEMPORARY);
	if (ret)
		goto err_unlock;

	/* FIXME: should be processed from 'struct ploop_mount_param' only ?? */
	char *t = di->runtime->component_name;
	snprintf(component_name, sizeof(component_name), "%s%s",
			holder_fd == NULL ? TSNAPSHOT_MOUNT_LOCK_MARK : "",
			param->component_name);
	di->runtime->component_name = component_name;

	mount_param.guid = param->guid;
	mount_param.target = param->target;
	ret = mount_image(di, &mount_param);
	di->runtime->component_name = t;

	if (ret)
		goto err_merge;

	strncpy(param->device, mount_param.device, sizeof(param->device));
	param->device[sizeof(param->device) - 1] = '\0';

	if (holder_fd != NULL) {
		ret = open_snap_holder(param->device, holder_fd);
		if (ret)
			goto err;
	}

	ploop_unlock_dd(di);

	return 0;

err:
	ploop_umount(mount_param.device, di);

err_merge:
	ploop_merge_snapshot_by_guid(di, di->top_guid, NULL);

err_unlock:
	ploop_unlock_dd(di);

	return ret;
}

int is_device_inuse(const char *dev)
{
	int count;
	char fname[PATH_MAX];
	char cookie[PLOOP_COOKIE_SIZE] = "";

	if (ploop_get_attr(dev, "open_count", &count))
		return 1;

	/* detect if snapshot locked by ploop mount */
	snprintf(fname, sizeof(fname), "/sys/block/%s/pstate/cookie",
			memcmp(dev, "/dev/", 5) == 0 ? dev + 5 : dev);
	if (read_line_quiet(fname, cookie, sizeof(cookie)))
		return 1;

	if (!strncmp(cookie, TSNAPSHOT_MOUNT_LOCK_MARK,
				sizeof(TSNAPSHOT_MOUNT_LOCK_MARK)-1))
		return 0;

	/* snap holder + mount */
	if (count >= 2)
		return 1;

	/* if there single reference we should detect is holder is alive */
	if (count == 1 && ploop_get_mnt_by_dev(dev, fname, sizeof(fname)) != 0)
		return 1;

	return 0;
}
예제 #5
0
파일: snapshot.c 프로젝트: OpenVZ/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;
}
예제 #6
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;
}
예제 #7
0
파일: snapshot.c 프로젝트: OpenVZ/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
 */
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) || guid == NULL)
		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_get_child_by_uuid(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, strdupa(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
int ploop_copy_stop(struct ploop_copy_handle *h)
{
	int ret;
	struct ploop_copy_stat stat = {};
	int iter;

	ploop_log(3, "pcopy last");

	ret = freeze_fs(h);
	if (ret)
		goto err;

	iter = 1;
	for (;;) {
		ret = ploop_copy_next_iteration(h, &stat);
		if (ret)
			goto err;
		else if (stat.xferred == 0)
			break;
		if (iter++ > 2) {
			ploop_err(0, "Too many iterations on frozen FS, aborting");
			return SYSEXIT_LOOP;
		}
	}

	/* Must clear dirty flag on ploop1 image. */
	if (!h->raw) {
		struct ploop_pvd_header *vh = get_free_iobuf(h);

		if (PREAD(&h->idelta, vh, 4096, 0)) {
			ret = SYSEXIT_READ;
			goto err;
		}

		vh->m_DiskInUse = 0;

		ploop_dbg(4, "Update header");

		ret = send_buf(h, vh, 4096, 0);
		if (ret)
			goto err;
	}

	ploop_dbg(4, "IOCTL TRACK_STOP");
	ret = ioctl(h->devfd, PLOOP_IOC_TRACK_STOP, 0);
	if (ret)
		goto err;

	h->tracker_on = 0;

	ploop_dbg(4, "SEND 0 0 (close)");
	send_async(h, NULL, 0, 0);

	pthread_join(h->send_th, NULL);
	h->send_th = 0;

	ploop_dbg(4, "pcopy stop done");

err:
	ploop_copy_release(h);

	return ret;
}
예제 #9
0
파일: snapshot.c 프로젝트: OpenVZ/ploop
int create_snapshot(const char *device, const char *delta, int syncfs,
		 const __u8 *cbt_u, const char *prev_delta)
{
	int ret;
	int lfd = -1;
	int fd = -1;
	off_t bdsize;
	struct ploop_ctl_delta req;
	__u32 blocksize;
	int version;
	void *or_data = NULL;

	ret = get_image_param_online(device, &bdsize,
			&blocksize, &version);
	if (ret)
		return ret;

	lfd = open(device, O_RDONLY|O_CLOEXEC);
	if (lfd < 0) {
		ploop_err(errno, "Can't open device %s", device);
		return SYSEXIT_DEVICE;
	}

	fd = create_snapshot_delta(delta, blocksize, bdsize, version);
	if (fd < 0) {
		ret = SYSEXIT_OPEN;
		goto err;
	}

	memset(&req, 0, sizeof(req));

	req.c.pctl_format = PLOOP_FMT_PLOOP1;
	req.c.pctl_flags = syncfs ? PLOOP_FLAG_FS_SYNC : 0;
	req.c.pctl_cluster_log = ffs(blocksize) - 1;
	req.c.pctl_size = 0;
	req.c.pctl_chunks = 1;
	req.f.pctl_type = PLOOP_IO_AUTO;

	if (cbt_u != NULL) {
		if ((ret = cbt_snapshot_prepare(lfd, cbt_u, &or_data))) {
			unlink(delta);
			goto err;
		}
	}

	ploop_log(0, "Creating snapshot dev=%s img=%s", device, delta);
	ret = create_snapshot_ioctl(lfd, fd, &req);
	if (ret) {
		unlink(delta);
		goto err;
	}

	if (cbt_u != NULL) {
		if ((ret = cbt_snapshot(lfd, cbt_u, prev_delta, or_data))) {
			unlink(delta);
			goto err;
		}
	}

err:
	free(or_data);

	if (lfd >= 0)
		close(lfd);
	if (fd >= 0)
		close(fd);

	return ret;
}
예제 #10
0
int ploop_copy_init(struct ploop_disk_images_data *di,
		struct ploop_copy_param *param,
		struct ploop_copy_handle **h)
{
	int ret, err;
	int blocksize;
	char *image = NULL;
	char *format = NULL;
	char device[64];
	struct ploop_copy_handle  *_h = NULL;
	int is_remote;
	char mnt[PATH_MAX];

	is_remote = is_fd_socket(param->ofd);
	if (is_remote < 0) {
		ploop_err(0, "Invalid output fd %d: must be a file, "
				"a pipe or a socket", param->ofd);
		return SYSEXIT_PARAM;
	}

	if (param->ofd == STDOUT_FILENO)
		ploop_set_verbose_level(PLOOP_LOG_NOSTDOUT);
	else if (param->ofd == STDERR_FILENO)
		ploop_set_verbose_level(PLOOP_LOG_NOCONSOLE);

	if (ploop_lock_dd(di))
		return SYSEXIT_LOCK;

	if (ploop_find_dev_by_dd(di, device, sizeof(device))) {
		ploop_err(0, "Can't find running ploop device");
		ret = SYSEXIT_SYS;
		goto err;
	}

	ret = get_image_info(device, &image, &format, &blocksize);
	if (ret)
		goto err;

	ploop_log(0, "Send image %s device=%s fmt=%s blocksize=%d local=%d",
			image, device, format, blocksize, !is_remote);

	_h = alloc_ploop_copy_handle(S2B(blocksize));
	if (_h == NULL) {
		ploop_err(0, "alloc_ploop_copy_handle");
		ret = SYSEXIT_MALLOC;
		goto err;
	}

	_h->raw = strcmp(format, "raw") == 0;
	_h->ofd = param->ofd;
	_h->is_remote = is_remote;

	_h->devfd = open(device, O_RDONLY);
	if (_h->devfd == -1) {
		ploop_err(errno, "Can't open device %s", device);
		ret = SYSEXIT_DEVICE;
		goto err;
	}
	fcntl(_h->devfd, F_SETFD, FD_CLOEXEC);

	ret = SYSEXIT_OPEN;
	err = ploop_get_mnt_by_dev(device, mnt, sizeof(mnt));
	if (err == -1)
		goto err;
	else if (ret == 0) {
		_h->mntfd = open(mnt, O_RDONLY);
		if (_h->mntfd < 0) {
			ploop_err(errno, "Can't open %s", mnt);
			goto err;
		}
	}

	if (open_delta_simple(&_h->idelta, image, O_RDONLY|O_DIRECT, OD_NOFLAGS)) {
		ret = SYSEXIT_OPEN;
		goto err;
	}

	ret = complete_running_operation(di, device);
	if (ret)
		goto err;

	_h->cl = register_cleanup_hook(cancel_sender, _h);

	pthread_mutex_lock(&_h->sd.wait_mutex);
err:
	if (ret) {
		ploop_copy_release(_h);
		free_ploop_copy_handle(_h);
	} else
		*h = _h;

	free(image);
	ploop_unlock_dd(di);

	return ret;
}
예제 #11
0
int ploop_copy_next_iteration(struct ploop_copy_handle *h,
		struct ploop_copy_stat *stat)
{
	struct ploop_track_extent e;
	int ret = 0;
	int done = 0;
	__u64 pos;
	__u64 iterpos = 0;

	stat->xferred = 0;
	ploop_dbg(3, "pcopy iter %d", h->niter);
	do {
		if (ioctl(h->devfd, PLOOP_IOC_TRACK_READ, &e)) {
			if (errno == EAGAIN) /* no more dirty blocks */
				break;

			ploop_err(errno, "PLOOP_IOC_TRACK_READ");
			ret = SYSEXIT_DEVIOC;
			goto err;
		}

		if (e.end > h->trackend)
			h->trackend = e.end;

		if (e.start < iterpos)
			done = 1;

		iterpos = e.end;
		stat->xferred += e.end - e.start;

		for (pos = e.start; pos < e.end; ) {
			ssize_t n;
			int copy = e.end - pos;

			if (copy > h->cluster)
				copy = h->cluster;

			if (pos + copy > h->trackpos) {
				h->trackpos = pos + copy;
				if (ioctl(h->devfd, PLOOP_IOC_TRACK_SETPOS, &h->trackpos)) {
					ploop_err(errno, "PLOOP_IOC_TRACK_SETPOS");
					ret = SYSEXIT_DEVIOC;
					goto err;
				}
			}

			ret = send_image_block(h, copy, pos, &n);
			if (ret)
				goto err;
			if (n != copy) {
				ploop_err(errno, "Short read");
				ret = SYSEXIT_READ;
				goto err;
			}

			pos += n;
		}
	} while (!done);

	wait_sender(h);

	/* sync after each iteration */
	ret = send_cmd(h, PCOPY_CMD_SYNC);
	if (ret)
		goto err;

	stat->xferred_total += stat->xferred;

	ploop_log(3, "pcopy iter %d xferred=%" PRIu64,
			h->niter++, (uint64_t)stat->xferred);

	return 0;

err:
	ploop_copy_release(h);
	return ret;
}
예제 #12
0
int ploop_copy_receiver(struct ploop_copy_receive_param *arg)
{
	int ofd, ret;
	__u64 cluster = 0;
	void *iobuf = NULL;
	int n;
	struct pcopy_pkt_desc desc;

	if (!arg)
		return SYSEXIT_PARAM;

	if (is_fd_socket(arg->ifd) != 1) {
		ploop_err(errno, "Invalid input fd %d: must be "
				"a pipe or a socket", arg->ifd);
		return SYSEXIT_PARAM;
	}

	ofd = open(arg->file, O_WRONLY|O_CREAT, 0600);
	if (ofd < 0) {
		ploop_err(errno, "Can't open %s", arg->file);
		return SYSEXIT_CREAT;
	}

	ploop_dbg(4, "RCV start %s", arg->file);
	for (;;) {
		if (nread(arg->ifd, &desc, sizeof(desc)) < 0) {
			ploop_err(errno, "Error in nread(desc)");
			ret = SYSEXIT_READ;
			goto out;
		}

		if (desc.marker != PCOPY_MARKER) {
			ploop_err(0, "Stream corrupted");
			ret = SYSEXIT_PROTOCOL;
			goto out;
		}

		if (desc.size > cluster) {
			free(iobuf);
			iobuf = NULL;
			cluster = desc.size;
			if (p_memalign(&iobuf, 4096, cluster)) {
				ret = SYSEXIT_MALLOC;
				goto out;
			}
		}

		if (desc.size == 0)
			break;

		if (nread(arg->ifd, iobuf, desc.size)) {
			ploop_err(errno, "Error in nread data");
			ret = SYSEXIT_READ;
			goto out;
		}

		ploop_log(3, "RCV type=%d len=%d pos=%" PRIu64,
				desc.type, desc.size, (uint64_t)desc.pos);
		switch (desc.type) {
		case PCOPY_PKT_DATA: {
			n = pwrite(ofd, iobuf, desc.size, desc.pos);
			if (n != desc.size) {
				if (n < 0)
					ploop_err(errno, "Error in pwrite");
				else
					ploop_err(0, "Error: short pwrite");
				ret = SYSEXIT_WRITE;
				goto out;
			}
			break;
		}
		case PCOPY_PKT_CMD: {
			unsigned int cmd = ((unsigned int *) iobuf)[0];
			switch(cmd) {
			case PCOPY_CMD_SYNC:
				ret = data_sync(ofd);
				if (ret)
					goto out;
				break;
			default:
				ploop_err(0, "ploop_copy_receiver: unsupported command %d",
						cmd);
				ret = SYSEXIT_PARAM;
				goto out;
			}
			break;
		}
		default:
			ploop_err(0, "ploop_copy_receiver: unsupported command type%d",
						desc.type);
			ret = SYSEXIT_PARAM;
			goto out;
		}

		/* send reply */
		ret = 0;
		if (nwrite(arg->ifd, &ret, sizeof(int))) {
			ret = SYSEXIT_WRITE;
			ploop_err(errno, "failed to send reply");
			goto out;
		}
	}

	ret = data_sync(ofd);
	if (ret)
		goto out;

	ploop_dbg(4, "RCV exited");
	/* send final reply */
	ret = 0;
	if (nwrite(arg->ifd, &ret, sizeof(int))) {
		ret = SYSEXIT_WRITE;
		ploop_err(errno, "failed to send reply");
		goto out;
	}

out:
	if (close(ofd)) {
		ploop_err(errno, "Error in close");
		if (!ret)
			ret = SYSEXIT_WRITE;
	}
	if (ret)
		unlink(arg->file);
	free(iobuf);

	return ret;
}
예제 #13
0
파일: snapshot.c 프로젝트: grze/ploop
static int do_create_snapshot(struct ploop_disk_images_data *di,
		const char *guid, 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;
	} 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;

	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;
}
예제 #14
0
파일: xml.c 프로젝트: kolyshkin/ploop
int ploop_store_diskdescriptor(const char *fname, struct ploop_disk_images_data *di)
{
	int i, rc = -1;
	xmlTextWriterPtr writer = NULL;
	xmlDocPtr doc = NULL;
	char tmp[PATH_MAX];
	char basedir[PATH_MAX];
	FILE *fp = NULL;

	ploop_log(0, "Storing %s", fname);

	if (convert_disk_descriptor(di))
		return -1;

	if (di->runtime->xml_fname == NULL)
		di->runtime->xml_fname = strdup(fname);

	get_basedir(fname, tmp, sizeof(tmp));
	if (tmp[0] == '\0')
		strcpy(tmp, "./");

	if (realpath(tmp, basedir) == NULL) {
		ploop_err(errno, "Can't resolve %s", tmp);
		return -1;
	}

	doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
	if (doc == NULL) {
		ploop_err(0, "Error creating xml document tree");
		return -1;
	}

	/* Create a new XmlWriter for DOM tree, with no compression. */
	writer = xmlNewTextWriterTree(doc, NULL, 0);
	if (writer == NULL) {
		ploop_err(0, "Error creating xml writer");
		goto err;
	}

	/* Start the document with the xml default for the version,
	 * encoding ISO 8859-1 and the default for the standalone
	 * declaration. */
	rc = xmlTextWriterStartDocument(writer, NULL, NULL, NULL);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriterStartDocument");
		goto err;
	}
	rc = xmlTextWriterStartElement(writer, BAD_CAST "Parallels_disk_image");
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriterStartDocument");
		goto err;
	}
	/*********************************************
	 *	Disk_Parameters
	 ********************************************/
	rc = xmlTextWriterStartElement(writer, BAD_CAST "Disk_Parameters");
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Disk_Parameters");
		goto err;
	}
	rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Disk_size", "%llu", di->size);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Disk_size");
		goto err;
	}

	if (di->max_delta_size != 0) {
		rc = xmlTextWriterWriteFormatElement(writer,
				BAD_CAST "Max_delta_size", "%llu", di->max_delta_size);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter Max_delta_size");
			goto err;
		}
	}

	rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Cylinders", "%u", di->cylinders);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Cylinders");
		goto err;
	}
	rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Heads", "%u", di->heads);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Heads");
		goto err;
	}
	rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Sectors", "%llu",
			di->size /(di->cylinders * di->heads));
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Sectors");
		goto err;
	}
	rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Padding", "%u", 0);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Padding");
		goto err;
	}

	/* Close   Disk_Parameters */
	rc = xmlTextWriterEndElement(writer);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriterEndElement");
		goto err;
	}
	/****************************************
	 * StorageData
	 ****************************************/
	rc = xmlTextWriterStartElement(writer, BAD_CAST "StorageData");
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter StorageData");
		goto err;
	}
	/* Start an element named "Storage" as child of StorageData. */
	rc = xmlTextWriterStartElement(writer, BAD_CAST "Storage");
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Storage");
		goto err;
	}
	rc = xmlTextWriterWriteElement(writer, BAD_CAST "Start",  BAD_CAST "0");
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Start");
		goto err;
	}
	rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "End", "%llu", di->size);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter End");
		goto err;
	}
	rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Blocksize", "%d",
			di->blocksize);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Blocksize");
		goto err;
	}
	if (di->mode == PLOOP_EXPANDED_PREALLOCATED_MODE) {
		rc = xmlTextWriterWriteElement(writer, BAD_CAST "Preallocated",
				BAD_CAST "1");
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter Preallocated");
			goto err;
		}
	}
	/****************************************
	 *	Images
	 ****************************************/
	for (i = 0; i < di->nimages; i++) {
		rc = xmlTextWriterStartElement(writer, BAD_CAST "Image");
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter Image");
			goto err;
		}

		rc = xmlTextWriterWriteElement(writer, BAD_CAST "GUID",
				BAD_CAST di->images[i]->guid);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter GUID");
			goto err;
		}
		rc = xmlTextWriterWriteElement(writer, BAD_CAST "Type",
			BAD_CAST (di->mode == PLOOP_RAW_MODE ? "Plain" : "Compressed"));
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter Type");
			goto err;
		}

		normalize_image_name(basedir, di->images[i]->file, tmp, sizeof(tmp));
		rc = xmlTextWriterWriteElement(writer, BAD_CAST "File",
				BAD_CAST tmp);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter File");
			goto err;
		}

		/*  Close  Image */
		rc = xmlTextWriterEndElement(writer);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriterEndElement");
			goto err;
		}
	}

	/* Close Storage */
	rc = xmlTextWriterEndElement(writer);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriterEndElement");
		goto err;
	}
	/* Close StorageData. */
	rc = xmlTextWriterEndElement(writer);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriterEndElement");
		goto err;
	}
	/****************************************
	 *	Snapshots
	 ****************************************/
	rc = xmlTextWriterStartElement(writer, BAD_CAST "Snapshots");
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriter Snapshots");
		goto err;
	}

	if (di->top_guid != NULL) {
		rc = xmlTextWriterWriteElement(writer, BAD_CAST "TopGUID",
				BAD_CAST di->top_guid);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter TopGUID");
			goto err;
		}
	}

	/****************************************
	 *      Shot
	 ****************************************/
	for (i = 0; i < di->nsnapshots; i++) {
		rc = xmlTextWriterStartElement(writer, BAD_CAST "Shot");
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter Shot");
			goto err;
		}

		rc = xmlTextWriterWriteElement(writer, BAD_CAST "GUID",
				BAD_CAST di->snapshots[i]->guid);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriterWrite GUID");
			goto err;
		}
		rc = xmlTextWriterWriteElement(writer, BAD_CAST "ParentGUID",
				BAD_CAST di->snapshots[i]->parent_guid);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriter ParentGUID");
			goto err;
		}

		if (di->snapshots[i]->temporary) {
			rc = xmlTextWriterWriteElement(writer, BAD_CAST "Temporary",  BAD_CAST "");
			if (rc < 0) {
				ploop_err(0, "Error at xmlTextWriter Temporary");
				goto err;
			}
		}

		/*  Close Shot */
		rc = xmlTextWriterEndElement(writer);
		if (rc < 0) {
			ploop_err(0, "Error at xmlTextWriterEndElement");
			goto err;
		}
	}
	/* Close Snapshots */
	rc = xmlTextWriterEndElement(writer);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriterEndElement");
		goto err;
	}

	/* Close Parallels_disk_image */
	rc = xmlTextWriterEndElement(writer);
	if (rc < 0) {
		ploop_err(0, "Error at xmlTextWriterEndElement");
		goto err;
	}
	xmlFreeTextWriter(writer);
	writer = NULL;

	snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
	fp = fopen(tmp, "w+");
	if (fp == NULL) {
		ploop_err(errno, "Can't open %s", tmp);
		goto err;
	}

	rc = xmlDocFormatDump(fp, doc, 1);
	if (rc < 0) {
		ploop_err(0, "Error at xmlDocFormatDump %s", tmp);
		goto err;
	}

	rc = fsync(fileno(fp));
	if (rc) {
		ploop_err(errno, "Failed to sync %s", tmp);
		goto err;
	}
	fclose(fp);
	fp = NULL;

	rc = rename(tmp, fname);
	if (rc < 0) {
		ploop_err(errno, "Can't rename %s to %s", tmp, fname);
		goto err;
	}

	rc = 0;
err:
	if (fp)
		fclose(fp);

	if (writer)
		xmlFreeTextWriter(writer);
	if (doc)
		xmlFreeDoc(doc);
	if (rc)
		return SYSEXIT_DISKDESCR;

	return 0;
}
예제 #15
0
파일: xml.c 프로젝트: kolyshkin/ploop
static int parse_xml(const char *basedir, xmlNode *root_node, struct ploop_disk_images_data *di)
{
	xmlNode *cur_node, *node;
	char image[PATH_MAX];
	const char *data = NULL;
	const char *file = NULL;
	const char *guid = NULL;
	const char *parent_guid = NULL;
	__u64 val;
	int is_preallocated = 0;
	int mode = PLOOP_EXPANDED_MODE;
	int n;

	cur_node = seek(root_node, "/Disk_Parameters");
	ERR(cur_node, "/Disk_Parameters");

	node = seek(cur_node, "Disk_size");
	if (node != NULL) {
		data = get_element_txt(node);
		if (parse_ul(data, &val) == 0)
			di->size = val;
	}

	node = seek(cur_node, "Max_delta_size");
	if (node != NULL) {
		data = get_element_txt(node);
		if (parse_ul(data, &val) == 0)
			di->max_delta_size = val;
	}

	node = seek(cur_node, "Cylinders");
	if (node != NULL) {
		data = get_element_txt(node);
		if (parse_ul(data, &val) == 0)
			di->cylinders = (unsigned)val;
	}

	node = seek(cur_node, "Heads");
	if (node != NULL) {
		data = get_element_txt(node);
		if (parse_ul(data, &val) == 0)
			di->heads = (unsigned)val;
	}
	node = seek(cur_node, "Sectors");
	if (node != NULL) {
		data = get_element_txt(node);
		if (parse_ul(data, &val) == 0)
			di->sectors= (unsigned)val;
	}
	cur_node = seek(root_node, "/StorageData/Storage");
	ERR(cur_node, "/StorageData/Storage");
	for (n = 0; cur_node; cur_node = cur_node->next, n++) {
		if (cur_node->type != XML_ELEMENT_NODE)
			continue;

		if (n > 0) {
			ploop_err(0, "Invalid disk descriptor file format:"
				" splitted disk is not supported");
			return -1;
		}

		node = seek(cur_node, "Blocksize");
		if (node != NULL) {
			data = get_element_txt(node);
			if (parse_ul(data, &val)) {
				ploop_err(0, "Invalid disk descriptor file format:"
						" Invalid Blocksize %s", data);
				return -1;
			}
			di->blocksize = (unsigned)val;
		}
		node = seek(cur_node, "Preallocated");
		if (node != NULL) {
			data = get_element_txt(node);
			if (parse_ul(data, &val) != 0 || val > 1) {
				ploop_err(0, "Invalid disk descriptor file format:"
						" Invalid Preallocated tag");
				return -1;
			}
			is_preallocated = val;
		}
	}

	cur_node = seek(root_node, "/StorageData/Storage/Image");
	ERR(cur_node, "/StorageData/Storage/Image");
	for (; cur_node; cur_node = cur_node->next) {
		if (cur_node->type != XML_ELEMENT_NODE)
			continue;
		guid = NULL;
		node = seek(cur_node, "GUID");
		if (node != NULL) {
			guid = get_element_txt(node);
			if (guid == NULL)
				guid = "";
		}
		ERR(guid, "GUID");
		node = seek(cur_node, "Type");
		if (node != NULL) {
			data = get_element_txt(node);
			if (data != NULL && !strcmp(data, "Plain"))
				mode = PLOOP_RAW_MODE;
		}
		file = NULL;
		node = seek(cur_node, "File");
		if (node != NULL) {
			file = get_element_txt(node);
			if (file != NULL) {
				if (basedir[0] != 0 && file[0] != '/')
					snprintf(image, sizeof(image), "%s%s", basedir, file);
				else
					snprintf(image, sizeof(image), "%s", file);
			}
		}
		ERR(file, "File");

		if (ploop_add_image_entry(di, image, guid))
			return -1;
	}

	if (is_preallocated) {
		if (mode == PLOOP_RAW_MODE) {
			ploop_err(0, "Invalid disk descriptor file format:"
				" Preallocated is not compatible with Plain image");
			return -1;
		}
		di->mode = PLOOP_EXPANDED_PREALLOCATED_MODE;
	} else {
		di->mode = mode;
	}

	cur_node = seek(root_node, "/Snapshots");
	ERR(cur_node, "/Snapshots");

	node = seek(cur_node, "TopGUID");
	if (node != NULL) {
		data = get_element_txt(node);
		ERR(data, "TopGUID");
		di->top_guid = strdup(data);
	}

	cur_node = seek(root_node, "/Snapshots/Shot");
	if (cur_node != NULL) {
		for (; cur_node; cur_node = cur_node->next) {
			int temporary = 0;

			if (cur_node->type != XML_ELEMENT_NODE)
				continue;

			guid = NULL;
			node = seek(cur_node, "GUID");
			if (node != NULL)
				guid = get_element_txt(node);
			ERR(guid, "Snapshots GUID");

			parent_guid = NULL;
			node = seek(cur_node, "ParentGUID");
			if (node != NULL)
				parent_guid = get_element_txt(node);

			ERR(parent_guid, "ParentGUID");

			node = seek(cur_node, "Temporary");
			if (node != NULL)
				temporary = 1;

			if (ploop_add_snapshot_entry(di, guid, parent_guid, temporary))
				return -1;
		}
	}
	return 0;
}
예제 #16
0
파일: di.c 프로젝트: avagin/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;
	}

	/* 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;
}