diff --git a/src/basic/chase.c b/src/basic/chase.c index 8fd7e67559..dba3a61687 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#include #include #include "alloc-util.h" @@ -370,8 +371,22 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int continue; } - /* Otherwise let's see what this is. */ - child = r = RET_NERRNO(openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH)); + /* Otherwise let's pin it by file descriptor, via O_PATH. Sounds pretty obvious to do this, + * right? You just do open() with O_PATH, and there you go. But uh, it's not that + * easy. open() via O_PATH does not trigger automounts, but we usually want that (except if + * CHASE_NO_AUTOFS is used). But thankfully there's a way out: the newer open_tree() call, + * when specified without OPEN_TREE_CLONE actually is fully equivalent to open() with O_PATH + * – except for one thing: it triggers automounts. + * + * As it turns out some sandboxes prohibit open_tree(), and return EPERM or ENOSYS if we call + * it. But since autofs does not work inside of mount namespace anyway, let's simply handle + * this as gracefully as we can, and fall back to classic openat() if we see EPERM/ENOSYS. */ + if (FLAGS_SET(flags, CHASE_NO_AUTOFS)) + r = -EPERM; + else + child = r = RET_NERRNO(open_tree(fd, first, AT_SYMLINK_NOFOLLOW|OPEN_TREE_CLOEXEC)); + if (r == -EPERM || ERRNO_IS_NEG_NOT_SUPPORTED(r)) + child = r = RET_NERRNO(openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH)); if (r < 0) { if (r != -ENOENT) return r;