Skip to content

Commit 1b8acfa

Browse files
osandovmaharmstone
authored andcommitted
libbtrfsutil: don't check for UID 0 in subvolume iterator
The subvolume iterator API explicitly checks whether geteuid() == 0 to decide whether to use the unprivileged BTRFS_IOC_GET_SUBVOL_ROOTREF and BTRFS_IOC_INO_LOOKUP_USER ioctls or the privileged BTRFS_IOC_TREE_SEARCH ioctl. This breaks in user namespaces: $ unshare -r python3 -c 'import btrfsutil; print(list(btrfsutil.SubvolumeIterator("/home")))' Traceback (most recent call last): File "<string>", line 1, in <module> btrfsutil.BtrfsUtilError: [BtrfsUtilError 12 Errno 1] Could not search B-tree: Operation not permitted Instead of the explicit check, let's try the privileged mode first, and if it fails with a permission error, fall back to the unprivileged mode (which has been supported since Linux 4.18). Note that we have to try the privileged mode first, since even for privileged users, the unprivileged mode may omit some subvolumes that are hidden by filesystem mounts. Signed-off-by: Omar Sandoval <osandov@fb.com>
1 parent 5a11724 commit 1b8acfa

File tree

1 file changed

+28
-13
lines changed

1 file changed

+28
-13
lines changed

libbtrfsutil/subvolume.c

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@
3232

3333
#include "btrfsutil_internal.h"
3434

35-
static bool is_root(void)
36-
{
37-
return geteuid() == 0;
38-
}
39-
4035
/*
4136
* This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
4237
* a file descriptor and calling it, because fstat() and fstatfs() don't accept
@@ -807,7 +802,11 @@ struct search_stack_entry {
807802
};
808803

809804
struct btrfs_util_subvolume_iterator {
810-
bool use_tree_search;
805+
/*
806+
* 1 if using tree search, 0 if using unprivileged ioctls, -1 if not
807+
* determined yet.
808+
*/
809+
int use_tree_search;
811810
int fd;
812811
/* cur_fd is only used for subvolume_iterator_next_unprivileged(). */
813812
int cur_fd;
@@ -1009,14 +1008,14 @@ PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_iterator_fd(int fd,
10091008
{
10101009
struct btrfs_util_subvolume_iterator *iter;
10111010
enum btrfs_util_error err;
1012-
bool use_tree_search;
1011+
int use_tree_search;
10131012

10141013
if (flags & ~BTRFS_UTIL_SUBVOLUME_ITERATOR_MASK) {
10151014
errno = EINVAL;
10161015
return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
10171016
}
10181017

1019-
use_tree_search = top != 0 || is_root();
1018+
use_tree_search = top == 0 ? -1 : 1;
10201019
if (top == 0) {
10211020
err = btrfs_util_is_subvolume_fd(fd);
10221021
if (err)
@@ -1666,13 +1665,29 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_iterator_next(struct btrfs_uti
16661665
char **path_ret,
16671666
uint64_t *id_ret)
16681667
{
1668+
/*
1669+
* On the first iteration, iter->use_tree_search < 0. In that case, we
1670+
* try a tree search, and if it fails with a permission error, we fall
1671+
* back to the unprivileged ioctls.
1672+
*/
16691673
if (iter->use_tree_search) {
1670-
return subvolume_iterator_next_tree_search(iter, path_ret,
1671-
id_ret);
1672-
} else {
1673-
return subvolume_iterator_next_unprivileged(iter, path_ret,
1674-
id_ret);
1674+
enum btrfs_util_error err;
1675+
struct search_stack_entry *entry;
1676+
1677+
err = subvolume_iterator_next_tree_search(iter, path_ret,
1678+
id_ret);
1679+
if (iter->use_tree_search > 0)
1680+
return err;
1681+
1682+
if (err != BTRFS_UTIL_ERROR_SEARCH_FAILED || errno != EPERM) {
1683+
iter->use_tree_search = 1;
1684+
return err;
1685+
}
1686+
entry = iter->search_stack;
1687+
entry->id = entry->search.key.min_objectid;
1688+
iter->use_tree_search = 0;
16751689
}
1690+
return subvolume_iterator_next_unprivileged(iter, path_ret, id_ret);
16761691
}
16771692
PUBLIC enum btrfs_util_error btrfs_util_subvolume_iter_next(struct btrfs_util_subvolume_iterator *iter,
16781693
char **path_ret,

0 commit comments

Comments
 (0)