예제 #1
0
PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
							  struct btrfs_util_subvolume_info *subvol)
{
	enum btrfs_util_error err;

	if (id == 0) {
		err = btrfs_util_is_subvolume_fd(fd);
		if (err)
			return err;

		if (!is_root())
			return get_subvolume_info_unprivileged(fd, subvol);

		err = btrfs_util_subvolume_id_fd(fd, &id);
		if (err)
			return err;
	}

	if ((id < BTRFS_FIRST_FREE_OBJECTID && id != BTRFS_FS_TREE_OBJECTID) ||
	    id > BTRFS_LAST_FREE_OBJECTID) {
		errno = ENOENT;
		return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
	}

	return get_subvolume_info_privileged(fd, id, subvol);
}
예제 #2
0
PUBLIC enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
						     uint64_t *id_ret)
{
	enum btrfs_util_error err;
	int fd;

	fd = open(path, O_RDONLY);
	if (fd == -1)
		return BTRFS_UTIL_ERROR_OPEN_FAILED;

	err = btrfs_util_subvolume_id_fd(fd, id_ret);
	SAVE_ERRNO_AND_CLOSE(fd);
	return err;
}
예제 #3
0
/*
 * Check that a path that we opened is the subvolume which we expect. It may not
 * be if there is another filesystem mounted over a parent directory or the
 * subvolume itself.
 */
static enum btrfs_util_error check_expected_subvolume(int fd, int parent_fd,
						      uint64_t tree_id)
{
	struct btrfs_ioctl_fs_info_args parent_fs_info, fs_info;
	enum btrfs_util_error err;
	uint64_t id;
	int ret;

	/* Make sure it's a subvolume. */
	err = btrfs_util_is_subvolume_fd(fd);
	if (err == BTRFS_UTIL_ERROR_NOT_BTRFS ||
	    err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
		errno = ENOENT;
		return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
	} else if (err) {
		return err;
	}

	/* Make sure it's on the same filesystem. */
	ret = ioctl(parent_fd, BTRFS_IOC_FS_INFO, &parent_fs_info);
	if (ret == -1)
		return BTRFS_UTIL_ERROR_FS_INFO_FAILED;
	ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fs_info);
	if (ret == -1)
		return BTRFS_UTIL_ERROR_FS_INFO_FAILED;
	if (memcmp(parent_fs_info.fsid, fs_info.fsid, sizeof(fs_info.fsid)) != 0) {
		errno = ENOENT;
		return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
	}

	/* Make sure it's the subvolume that we expected. */
	err = btrfs_util_subvolume_id_fd(fd, &id);
	if (err)
		return err;
	if (id != tree_id) {
		errno = ENOENT;
		return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
	}

	return BTRFS_UTIL_OK;
}
예제 #4
0
PUBLIC enum btrfs_util_error btrfs_util_set_default_subvolume_fd(int fd,
								 uint64_t id)
{
	enum btrfs_util_error err;
	int ret;

	if (id == 0) {
		err = btrfs_util_is_subvolume_fd(fd);
		if (err)
			return err;

		err = btrfs_util_subvolume_id_fd(fd, &id);
		if (err)
			return err;
	}

	ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id);
	if (ret == -1)
		return BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED;

	return BTRFS_UTIL_OK;
}
예제 #5
0
PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_iterator_fd(int fd,
								     uint64_t top,
								     int flags,
								     struct btrfs_util_subvolume_iterator **ret)
{
	struct btrfs_util_subvolume_iterator *iter;
	enum btrfs_util_error err;

	if (flags & ~BTRFS_UTIL_SUBVOLUME_ITERATOR_MASK) {
		errno = EINVAL;
		return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
	}

	if (top == 0) {
		err = btrfs_util_is_subvolume_fd(fd);
		if (err)
			return err;

		err = btrfs_util_subvolume_id_fd(fd, &top);
		if (err)
			return err;
	}

	iter = malloc(sizeof(*iter));
	if (!iter)
		return BTRFS_UTIL_ERROR_NO_MEMORY;

	iter->fd = fd;
	iter->flags = flags;

	iter->search_stack_len = 0;
	iter->search_stack_capacity = 4;
	iter->search_stack = malloc(sizeof(*iter->search_stack) *
				    iter->search_stack_capacity);
	if (!iter->search_stack) {
		err = BTRFS_UTIL_ERROR_NO_MEMORY;
		goto out_iter;
	}

	iter->cur_path_capacity = 256;
	iter->cur_path = malloc(iter->cur_path_capacity);
	if (!iter->cur_path) {
		err = BTRFS_UTIL_ERROR_NO_MEMORY;
		goto out_search_stack;
	}

	err = append_to_search_stack(iter, top, 0);
	if (err)
		goto out_cur_path;

	*ret = iter;

	return BTRFS_UTIL_OK;

out_cur_path:
	free(iter->cur_path);
out_search_stack:
	free(iter->search_stack);
out_iter:
	free(iter);
	return err;
}
예제 #6
0
PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
							  struct btrfs_util_subvolume_info *subvol)
{
	struct btrfs_ioctl_search_args search = {
		.key = {
			.tree_id = BTRFS_ROOT_TREE_OBJECTID,
			.min_type = BTRFS_ROOT_ITEM_KEY,
			.max_type = BTRFS_ROOT_BACKREF_KEY,
			.min_offset = 0,
			.max_offset = UINT64_MAX,
			.min_transid = 0,
			.max_transid = UINT64_MAX,
			.nr_items = 0,
		},
	};
	enum btrfs_util_error err;
	size_t items_pos = 0, buf_off = 0;
	bool need_root_item = true, need_root_backref = true;
	int ret;

	if (id == 0) {
		err = btrfs_util_is_subvolume_fd(fd);
		if (err)
			return err;

		err = btrfs_util_subvolume_id_fd(fd, &id);
		if (err)
			return err;
	}

	if ((id < BTRFS_FIRST_FREE_OBJECTID && id != BTRFS_FS_TREE_OBJECTID) ||
	    id > BTRFS_LAST_FREE_OBJECTID) {
		errno = ENOENT;
		return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
	}

	search.key.min_objectid = search.key.max_objectid = id;

	if (subvol) {
		subvol->id = id;
		subvol->parent_id = 0;
		subvol->dir_id = 0;
		if (id == BTRFS_FS_TREE_OBJECTID)
			need_root_backref = false;
	} else {
		/*
		 * We only need the backref for filling in the subvolume info.
		 */
		need_root_backref = false;
	}

	/* Don't bother searching for the backref if we don't need it. */
	if (!need_root_backref)
		search.key.max_type = BTRFS_ROOT_ITEM_KEY;

	while (need_root_item || need_root_backref) {
		const struct btrfs_ioctl_search_header *header;

		if (items_pos >= search.key.nr_items) {
			search.key.nr_items = 4096;
			ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
			if (ret == -1)
				return BTRFS_UTIL_ERROR_SEARCH_FAILED;
			items_pos = 0;
			buf_off = 0;

			if (search.key.nr_items == 0) {
				if (need_root_item) {
					errno = ENOENT;
					return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
				} else {
					break;
				}
			}
		}

		header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
		if (header->type == BTRFS_ROOT_ITEM_KEY) {
			if (subvol) {
				const struct btrfs_root_item *root;

				root = (const struct btrfs_root_item *)(header + 1);
				copy_root_item(subvol, root);
			}
			need_root_item = false;
			search.key.min_type = BTRFS_ROOT_BACKREF_KEY;
		} else if (header->type == BTRFS_ROOT_BACKREF_KEY) {
			if (subvol) {
				const struct btrfs_root_ref *ref;

				ref = (const struct btrfs_root_ref *)(header + 1);
				subvol->parent_id = header->offset;
				subvol->dir_id = le64_to_cpu(ref->dirid);
			}
			need_root_backref = false;
			search.key.min_type = UINT32_MAX;
		}

		items_pos++;
		buf_off += sizeof(*header) + header->len;
	}

	return BTRFS_UTIL_OK;
}
예제 #7
0
PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd,
							uint64_t *id_ret)
{
	struct btrfs_ioctl_ino_lookup_args args = {
		.treeid = 0,
		.objectid = BTRFS_FIRST_FREE_OBJECTID,
	};
	int ret;

	ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
	if (ret == -1) {
		close(fd);
		return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
	}

	*id_ret = args.treeid;

	return BTRFS_UTIL_OK;
}

PUBLIC enum btrfs_util_error btrfs_util_subvolume_path(const char *path,
						       uint64_t id,
						       char **path_ret)
{
	enum btrfs_util_error err;
	int fd;

	fd = open(path, O_RDONLY);
	if (fd == -1)
		return BTRFS_UTIL_ERROR_OPEN_FAILED;

	err = btrfs_util_subvolume_path_fd(fd, id, path_ret);
	SAVE_ERRNO_AND_CLOSE(fd);
	return err;
}

PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
							  char **path_ret)
{
	char *path, *p;
	size_t capacity = 4096;

	if (id == 0) {
		enum btrfs_util_error err;

		err = btrfs_util_is_subvolume_fd(fd);
		if (err)
			return err;

		err = btrfs_util_subvolume_id_fd(fd, &id);
		if (err)
			return err;
	}

	path = malloc(capacity);
	if (!path)
		return BTRFS_UTIL_ERROR_NO_MEMORY;
	p = path + capacity - 1;
	p[0] = '\0';

	while (id != BTRFS_FS_TREE_OBJECTID) {
		struct btrfs_ioctl_search_args search = {
			.key = {
				.tree_id = BTRFS_ROOT_TREE_OBJECTID,
				.min_objectid = id,
				.max_objectid = id,
				.min_type = BTRFS_ROOT_BACKREF_KEY,
				.max_type = BTRFS_ROOT_BACKREF_KEY,
				.min_offset = 0,
				.max_offset = UINT64_MAX,
				.min_transid = 0,
				.max_transid = UINT64_MAX,
				.nr_items = 1,
			},
		};
		struct btrfs_ioctl_ino_lookup_args lookup;
		const struct btrfs_ioctl_search_header *header;
		const struct btrfs_root_ref *ref;
		const char *name;
		uint16_t name_len;
		size_t lookup_len;
		size_t total_len;
		int ret;

		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
		if (ret == -1) {
			free(path);
			return BTRFS_UTIL_ERROR_SEARCH_FAILED;
		}

		if (search.key.nr_items == 0) {
			free(path);
			errno = ENOENT;
			return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
		}

		header = (struct btrfs_ioctl_search_header *)search.buf;
		ref = (struct btrfs_root_ref *)(header + 1);
		name = (char *)(ref + 1);
		name_len = le16_to_cpu(ref->name_len);

		id = header->offset;

		lookup.treeid = id;
		lookup.objectid = le64_to_cpu(ref->dirid);
		ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup);
		if (ret == -1) {
			free(path);
			return BTRFS_UTIL_ERROR_SEARCH_FAILED;
		}
		lookup_len = strlen(lookup.name);

		total_len = name_len + lookup_len + (id != BTRFS_FS_TREE_OBJECTID);
		if (p - total_len < path) {
			char *new_path, *new_p;
			size_t new_capacity = capacity * 2;

			new_path = malloc(new_capacity);
			if (!new_path) {
				free(path);
				return BTRFS_UTIL_ERROR_NO_MEMORY;
			}
			new_p = new_path + new_capacity - (path + capacity - p);
			memcpy(new_p, p, path + capacity - p);
			free(path);
			path = new_path;
			p = new_p;
			capacity = new_capacity;
		}
		p -= name_len;
		memcpy(p, name, name_len);
		p -= lookup_len;
		memcpy(p, lookup.name, lookup_len);
		if (id != BTRFS_FS_TREE_OBJECTID)
			*--p = '/';
	}

	if (p != path)
		memmove(path, p, path + capacity - p);

	*path_ret = path;

	return BTRFS_UTIL_OK;
}