Exemplo n.º 1
0
static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
{
	struct subvol_info *si;

	si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
			subvol_search_by_root_id);
	if (!si)
		return NULL;

	si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
			subvol_search_by_uuid);
	if (!si)
		return NULL;
	return si;
}
Exemplo n.º 2
0
static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id)
{
	struct subvol_info *si_tmp;
	struct subvol_info *si;

	si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL,
			subvol_search_by_root_id);
	if (IS_ERR_OR_NULL(si_tmp))
		return si_tmp;

	si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL,
			subvol_search_by_uuid);
	free(si_tmp->path);
	free(si_tmp);
	return si;
}
Exemplo n.º 3
0
static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
{
	struct subvol_info *si;

	si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
			subvol_search_by_path);
	if (!si)
		return -ENOENT;
	*root_id = si->root_id;
	return 0;
}
Exemplo n.º 4
0
static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
{
	int ret;
	struct subvol_info *parent;
	struct subvol_info *parent2;
	struct subvol_info *best_parent = NULL;
	__s64 tmp;
	u64 best_diff = (u64)-1;
	int i;

	parent = get_parent(s, root_id);
	if (!parent) {
		ret = -ENOENT;
		goto out;
	}

	for (i = 0; i < s->clone_sources_count; i++) {
		if (s->clone_sources[i] == parent->root_id) {
			best_parent = parent;
			goto out_found;
		}
	}

	for (i = 0; i < s->clone_sources_count; i++) {
		parent2 = get_parent(s, s->clone_sources[i]);
		if (parent2 != parent)
			continue;

		parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
				0, NULL, subvol_search_by_root_id);

		tmp = parent2->ctransid - parent->ctransid;
		if (tmp < 0)
			tmp *= -1;
		if (tmp < best_diff) {
			best_parent = parent2;
			best_diff = tmp;
		}
	}

	if (!best_parent) {
		ret = -ENOENT;
		goto out;
	}

out_found:
	*found = best_parent->root_id;
	ret = 0;

out:
	return ret;
}
Exemplo n.º 5
0
static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
{
	struct subvol_info *si;

	si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path,
			subvol_search_by_path);
	if (IS_ERR_OR_NULL(si)) {
		if (!si)
			return -ENOENT;
		else
			return PTR_ERR(si);
	}
	*root_id = si->root_id;
	free(si->path);
	free(si);
	return 0;
}
static int process_clone(const char *path, u64 offset, u64 len,
			 const u8 *clone_uuid, u64 clone_ctransid,
			 const char *clone_path, u64 clone_offset,
			 void *user)
{
	int ret;
	struct btrfs_receive *r = user;
	struct btrfs_ioctl_clone_range_args clone_args;
	struct subvol_info *si = NULL;
	char full_path[PATH_MAX];
	char *subvol_path = NULL;
	char full_clone_path[PATH_MAX];
	int clone_fd = -1;

	ret = path_cat_out(full_path, r->full_subvol_path, path);
	if (ret < 0) {
		error("clone: source path invalid: %s", path);
		goto out;
	}

	ret = open_inode_for_write(r, full_path);
	if (ret < 0)
		goto out;

	si = subvol_uuid_search(&r->sus, 0, clone_uuid, clone_ctransid, NULL,
			subvol_search_by_received_uuid);
	if (!si) {
		if (memcmp(clone_uuid, r->cur_subvol.received_uuid,
				BTRFS_UUID_SIZE) == 0) {
			/* TODO check generation of extent */
			subvol_path = strdup(r->cur_subvol_path);
		} else {
			ret = -ENOENT;
			error("clone: did not find source subvol");
			goto out;
		}
	} else {
		/*if (rs_args.ctransid > rs_args.rtransid) {
			if (!r->force) {
				ret = -EINVAL;
				fprintf(stderr, "ERROR: subvolume %s was "
						"modified after it was "
						"received.\n",
						r->subvol_parent_name);
				goto out;
			} else {
				fprintf(stderr, "WARNING: subvolume %s was "
						"modified after it was "
						"received.\n",
						r->subvol_parent_name);
			}
		}*/
		subvol_path = strdup(si->path);
	}

	ret = path_cat_out(full_clone_path, subvol_path, clone_path);
	if (ret < 0) {
		error("clone: target path invalid: %s", clone_path);
		goto out;
	}

	clone_fd = openat(r->mnt_fd, full_clone_path, O_RDONLY | O_NOATIME);
	if (clone_fd < 0) {
		ret = -errno;
		error("cannot open %s: %s", full_clone_path, strerror(-ret));
		goto out;
	}

	clone_args.src_fd = clone_fd;
	clone_args.src_offset = clone_offset;
	clone_args.src_length = len;
	clone_args.dest_offset = offset;
	ret = ioctl(r->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args);
	if (ret < 0) {
		ret = -errno;
		error("failed to clone extents to %s\n%s\n",
				path, strerror(-ret));
		goto out;
	}

out:
	if (si) {
		free(si->path);
		free(si);
	}
	free(subvol_path);
	if (clone_fd != -1)
		close(clone_fd);
	return ret;
}
static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
			    const u8 *parent_uuid, u64 parent_ctransid,
			    void *user)
{
	int ret;
	struct btrfs_receive *r = user;
	char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
	struct btrfs_ioctl_vol_args_v2 args_v2;
	struct subvol_info *parent_subvol = NULL;

	ret = finish_subvol(r);
	if (ret < 0)
		goto out;

	BUG_ON(r->cur_subvol.path);
	BUG_ON(r->cur_subvol_path[0]);

	if (*r->dest_dir_path == 0) {
		strncpy_null(r->cur_subvol_path, path);
	} else {
		ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path);
		if (ret < 0) {
			error("snapshot: path invalid: %s", path);
			goto out;
		}
	}
	ret = path_cat3_out(r->full_subvol_path, r->root_path,
			r->dest_dir_path, path);
	if (ret < 0) {
		error("snapshot: path invalid: %s", path);
		goto out;
	}

	fprintf(stdout, "At snapshot %s\n", path);

	memcpy(r->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE);
	r->cur_subvol.stransid = ctransid;

	if (g_verbose) {
		uuid_unparse((u8*)r->cur_subvol.received_uuid, uuid_str);
		fprintf(stderr, "receiving snapshot %s uuid=%s, "
				"ctransid=%llu ", path, uuid_str,
				r->cur_subvol.stransid);
		uuid_unparse(parent_uuid, uuid_str);
		fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n",
				uuid_str, parent_ctransid);
	}

	memset(&args_v2, 0, sizeof(args_v2));
	strncpy_null(args_v2.name, path);

	parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
			parent_ctransid, NULL, subvol_search_by_received_uuid);
	if (!parent_subvol) {
		parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
				parent_ctransid, NULL, subvol_search_by_uuid);
	}
	if (!parent_subvol) {
		ret = -ENOENT;
		error("cannot find parent subvolume");
		goto out;
	}

	/*
	 * The path is resolved from the root subvol, but we could be in some
	 * subvolume under the root subvolume, so try and adjust the path to be
	 * relative to our root path.
	 */
	if (r->full_root_path) {
		size_t root_len;
		size_t sub_len;

		root_len = strlen(r->full_root_path);
		sub_len = strlen(parent_subvol->path);

		/* First make sure the parent subvol is actually in our path */
		if (sub_len < root_len ||
		    strstr(parent_subvol->path, r->full_root_path) == NULL) {
			error(
		"parent subvol is not reachable from inside the root subvol");
			ret = -ENOENT;
			goto out;
		}

		if (sub_len == root_len) {
			parent_subvol->path[0] = '/';
			parent_subvol->path[1] = '\0';
		} else {
			/*
			 * root path is foo/bar
			 * subvol path is foo/bar/baz
			 *
			 * we need to have baz be the path, so we need to move
			 * the bit after foo/bar/, so path + root_len + 1, and
			 * move the part we care about, so sub_len - root_len -
			 * 1.
			 */
			memmove(parent_subvol->path,
				parent_subvol->path + root_len + 1,
				sub_len - root_len - 1);
			parent_subvol->path[sub_len - root_len - 1] = '\0';
		}
	}
	/*if (rs_args.ctransid > rs_args.rtransid) {
		if (!r->force) {
			ret = -EINVAL;
			fprintf(stderr, "ERROR: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
			goto out;
		} else {
			fprintf(stderr, "WARNING: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
		}
	}*/

	if (*parent_subvol->path == 0)
		args_v2.fd = dup(r->mnt_fd);
	else
		args_v2.fd = openat(r->mnt_fd, parent_subvol->path,
				O_RDONLY | O_NOATIME);
	if (args_v2.fd < 0) {
		ret = -errno;
		if (errno != ENOENT)
			error("cannot open %s: %s",
					parent_subvol->path, strerror(-ret));
		else
			fprintf(stderr,
				"It seems that you have changed your default "
				"subvolume or you specify other subvolume to\n"
				"mount btrfs, try to remount this btrfs filesystem "
				"with fs tree, and run btrfs receive again!\n");
		goto out;
	}

	ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
	close(args_v2.fd);
	if (ret < 0) {
		ret = -errno;
		error("creating snapshot %s -> %s failed: %s",
				parent_subvol->path, path, strerror(-ret));
		goto out;
	}

out:
	if (parent_subvol) {
		free(parent_subvol->path);
		free(parent_subvol);
	}
	return ret;
}
Exemplo n.º 8
0
static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
			    const u8 *parent_uuid, u64 parent_ctransid,
			    void *user)
{
	int ret;
	struct btrfs_receive *r = user;
	char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
	struct btrfs_ioctl_vol_args_v2 args_v2;
	struct subvol_info *parent_subvol = NULL;

	ret = finish_subvol(r);
	if (ret < 0)
		goto out;

	r->cur_subvol = calloc(1, sizeof(*r->cur_subvol));

	if (strlen(r->dest_dir_path) == 0)
		r->cur_subvol->path = strdup(path);
	else
		r->cur_subvol->path = path_cat(r->dest_dir_path, path);
	free(r->full_subvol_path);
	r->full_subvol_path = path_cat3(r->root_path, r->dest_dir_path, path);

	fprintf(stdout, "At snapshot %s\n", path);

	memcpy(r->cur_subvol->received_uuid, uuid, BTRFS_UUID_SIZE);
	r->cur_subvol->stransid = ctransid;

	if (g_verbose) {
		uuid_unparse((u8*)r->cur_subvol->received_uuid, uuid_str);
		fprintf(stderr, "receiving snapshot %s uuid=%s, "
				"ctransid=%llu ", path, uuid_str,
				r->cur_subvol->stransid);
		uuid_unparse(parent_uuid, uuid_str);
		fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n",
				uuid_str, parent_ctransid);
	}

	memset(&args_v2, 0, sizeof(args_v2));
	strncpy_null(args_v2.name, path);

	parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
			parent_ctransid, NULL, subvol_search_by_received_uuid);
	if (!parent_subvol) {
		parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
				parent_ctransid, NULL, subvol_search_by_uuid);
	}
	if (!parent_subvol) {
		ret = -ENOENT;
		fprintf(stderr, "ERROR: could not find parent subvolume\n");
		goto out;
	}

	/*if (rs_args.ctransid > rs_args.rtransid) {
		if (!r->force) {
			ret = -EINVAL;
			fprintf(stderr, "ERROR: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
			goto out;
		} else {
			fprintf(stderr, "WARNING: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
		}
	}*/

	args_v2.fd = openat(r->mnt_fd, parent_subvol->path,
			O_RDONLY | O_NOATIME);
	if (args_v2.fd < 0) {
		ret = -errno;
		if (errno != ENOENT)
			fprintf(stderr, "ERROR: open %s failed. %s\n",
					parent_subvol->path, strerror(-ret));
		else
			fprintf(stderr,
				"It seems that you have changed your default "
				"subvolume or you specify other subvolume to\n"
				"mount btrfs, try to remount this btrfs filesystem "
				"with fs tree, and run btrfs receive again!\n");
		goto out;
	}

	ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
	close(args_v2.fd);
	if (ret < 0) {
		ret = -errno;
		fprintf(stderr, "ERROR: creating snapshot %s -> %s "
				"failed. %s\n", parent_subvol->path,
				path, strerror(-ret));
		goto out;
	}

out:
	if (parent_subvol) {
		free(parent_subvol->path);
		free(parent_subvol);
	}
	return ret;
}
Exemplo n.º 9
0
static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
{
	int ret;
	struct subvol_info *parent = NULL;
	struct subvol_info *parent2 = NULL;
	struct subvol_info *best_parent = NULL;
	u64 best_diff = (u64)-1;
	int i;

	parent = get_parent(sctx, root_id);
	if (IS_ERR_OR_NULL(parent)) {
		if (!parent)
			ret = -ENOENT;
		else
			ret = PTR_ERR(parent);
		goto out;
	}

	for (i = 0; i < sctx->clone_sources_count; i++) {
		if (sctx->clone_sources[i] == parent->root_id) {
			best_parent = parent;
			parent = NULL;
			goto out_found;
		}
	}

	for (i = 0; i < sctx->clone_sources_count; i++) {
		s64 tmp;

		parent2 = get_parent(sctx, sctx->clone_sources[i]);
		if (IS_ERR_OR_NULL(parent2))
			continue;
		if (parent2->root_id != parent->root_id) {
			free(parent2->path);
			free(parent2);
			parent2 = NULL;
			continue;
		}

		free(parent2->path);
		free(parent2);
		parent2 = subvol_uuid_search(&sctx->sus,
				sctx->clone_sources[i], NULL, 0, NULL,
				subvol_search_by_root_id);
		if (IS_ERR_OR_NULL(parent2)) {
			if (!parent2)
				ret = -ENOENT;
			else
				ret = PTR_ERR(parent2);
			goto out;
		}
		tmp = parent2->ctransid - parent->ctransid;
		if (tmp < 0)
			tmp = -tmp;
		if (tmp < best_diff) {
			if (best_parent) {
				free(best_parent->path);
				free(best_parent);
			}
			best_parent = parent2;
			parent2 = NULL;
			best_diff = tmp;
		} else {
			free(parent2->path);
			free(parent2);
			parent2 = NULL;
		}
	}

	if (!best_parent) {
		ret = -ENOENT;
		goto out;
	}

out_found:
	*found = best_parent->root_id;
	ret = 0;

out:
	if (parent) {
		free(parent->path);
		free(parent);
	}
	if (best_parent) {
		free(best_parent->path);
		free(best_parent);
	}
	return ret;
}
Exemplo n.º 10
0
static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
{
	int ret;
	pthread_t t_read;
	pthread_attr_t t_attr;
	struct btrfs_ioctl_send_args io_send;
	struct subvol_info *si;
	void *t_err = NULL;
	int subvol_fd = -1;
	int pipefd[2] = {-1, -1};

	si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
			subvol_search_by_root_id);
	if (!si) {
		ret = -ENOENT;
		fprintf(stderr, "ERROR: could not find subvol info for %llu",
				root_id);
		goto out;
	}

	subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
	if (subvol_fd < 0) {
		ret = -errno;
		fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
				strerror(-ret));
		goto out;
	}

	ret = pthread_attr_init(&t_attr);

	ret = pipe(pipefd);
	if (ret < 0) {
		ret = -errno;
		fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
		goto out;
	}

	memset(&io_send, 0, sizeof(io_send));
	io_send.send_fd = pipefd[1];
	send->send_fd = pipefd[0];

	if (!ret)
		ret = pthread_create(&t_read, &t_attr, dump_thread,
					send);
	if (ret) {
		ret = -ret;
		fprintf(stderr, "ERROR: thread setup failed: %s\n",
			strerror(-ret));
		goto out;
	}

	io_send.clone_sources = (__u64*)send->clone_sources;
	io_send.clone_sources_count = send->clone_sources_count;
	io_send.parent_root = parent_root_id;
	ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
	if (ret) {
		ret = -errno;
		fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
			strerror(-ret));
		goto out;
	}
	if (g_verbose > 0)
		fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);

	if (g_verbose > 0)
		fprintf(stderr, "joining genl thread\n");

	close(pipefd[1]);
	pipefd[1] = 0;

	ret = pthread_join(t_read, &t_err);
	if (ret) {
		ret = -ret;
		fprintf(stderr, "ERROR: pthread_join failed: %s\n",
			strerror(-ret));
		goto out;
	}
	if (t_err) {
		ret = (long int)t_err;
		fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
			"(%s)\n", (long int)t_err, strerror(-ret));
		goto out;
	}

	pthread_attr_destroy(&t_attr);

	ret = 0;

out:
	if (subvol_fd != -1)
		close(subvol_fd);
	if (pipefd[0] != -1)
		close(pipefd[0]);
	if (pipefd[1] != -1)
		close(pipefd[1]);
	return ret;
}
Exemplo n.º 11
0
static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
			    const u8 *parent_uuid, u64 parent_ctransid,
			    void *user)
{
	int ret;
	struct btrfs_receive *r = user;
	char uuid_str[128];
	struct btrfs_ioctl_vol_args_v2 args_v2;

	ret = finish_subvol(r);
	if (ret < 0)
		goto out;

	r->cur_subvol = calloc(1, sizeof(*r->cur_subvol));
	r->parent_subvol = NULL;

	r->cur_subvol->path = strdup(path);
	r->full_subvol_path = path_cat(r->root_path, path);

	fprintf(stderr, "At snapshot %s\n", path);

	memcpy(r->cur_subvol->received_uuid, uuid, BTRFS_UUID_SIZE);
	r->cur_subvol->stransid = ctransid;

	if (g_verbose) {
		uuid_unparse((u8*)r->cur_subvol->received_uuid, uuid_str);
		fprintf(stderr, "receiving snapshot %s uuid=%s, "
				"ctransid=%llu ", path, uuid_str,
				r->cur_subvol->stransid);
		uuid_unparse(parent_uuid, uuid_str);
		fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n",
				uuid_str, parent_ctransid);
	}

	memset(&args_v2, 0, sizeof(args_v2));
	strcpy(args_v2.name, path);

	r->parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
			parent_ctransid, NULL, subvol_search_by_received_uuid);
	if (!r->parent_subvol) {
		ret = -ENOENT;
		fprintf(stderr, "ERROR: could not find parent subvolume\n");
		goto out;
	}

	/*if (rs_args.ctransid > rs_args.rtransid) {
		if (!r->force) {
			ret = -EINVAL;
			fprintf(stderr, "ERROR: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
			goto out;
		} else {
			fprintf(stderr, "WARNING: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
		}
	}*/

	args_v2.fd = openat(r->mnt_fd, r->parent_subvol->path,
			O_RDONLY | O_NOATIME);
	if (args_v2.fd < 0) {
		ret = -errno;
		fprintf(stderr, "ERROR: open %s failed. %s\n",
				r->parent_subvol->path, strerror(-ret));
		goto out;
	}

	ret = ioctl(r->mnt_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
	close(args_v2.fd);
	if (ret < 0) {
		ret = -errno;
		fprintf(stderr, "ERROR: creating snapshot %s -> %s "
				"failed. %s\n", r->parent_subvol->path,
				path, strerror(-ret));
		goto out;
	}

out:
	return ret;
}