From 1320e562839337b309678416122790110dd5ad3a Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Thu, 3 Jul 2025 22:40:15 +0200 Subject: [PATCH] chase: introduce openat_opath_with_automount() helper Follow-up for c5de7b14ae2e08d267d8d75bc88934ac6aa7dcd6 chase() is arguably a hot path in our code, hence it deserves some caching whether open_tree() is available. Moreover, the manual set of r to -EPERM feels kinda ugly. Let's instead extract this bit into its own function. --- src/basic/chase.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/basic/chase.c b/src/basic/chase.c index dba3a616876..fe060324ef5 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -77,6 +77,34 @@ static int log_prohibited_symlink(int fd, ChaseFlags flags) { strna(n1)); } +static int openat_opath_with_automount(int dir_fd, const char *path, bool automount) { + static bool can_open_tree = true; + int r; + + /* Pin an inode via O_PATH semantics. 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. */ + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + + if (automount && can_open_tree) { + r = RET_NERRNO(open_tree(dir_fd, path, AT_SYMLINK_NOFOLLOW|OPEN_TREE_CLOEXEC)); + if (r >= 0 || (r != -EPERM && !ERRNO_IS_NEG_NOT_SUPPORTED(r))) + return r; + + can_open_tree = false; + } + + return RET_NERRNO(openat(dir_fd, path, O_PATH|O_NOFOLLOW|O_CLOEXEC)); +} + static int chaseat_needs_absolute(int dir_fd, const char *path) { if (dir_fd < 0) return path_is_absolute(path); @@ -371,22 +399,8 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int continue; } - /* 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)); + /* Otherwise let's pin it by file descriptor, via O_PATH. */ + child = r = openat_opath_with_automount(fd, first, /* automount = */ !FLAGS_SET(flags, CHASE_NO_AUTOFS)); if (r < 0) { if (r != -ENOENT) return r; @@ -417,6 +431,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int return r; } + /* ... and then check what it actually is. */ if (fstat(child, &st_child) < 0) return -errno; -- 2.47.3