chase: trigger automount only when explicitly requested

Since c5de7b14ae
file searching implies a new mount api syscall by default,
to trigger automounts.

But, this is not necessary in most cases, e.g. when chasing
syspath in sd-device (actually this causes regression in umockdev,
see https://github.com/martinpitt/umockdev/issues/271).
Another example is reading unit files, especially .network files,
as automount may trigger mounting network filesystems...

Also, when this is used in NSS plugins, programs that load the
plugins may fail because of spuriously configured seccomp. See #38565.

Let's not trigger automount by default, and do only when explicitly
requested.

This introduces CHASE_TRIGGER_AUTOFS, and use it in
- service manager,
- bootctl and finding ESP/xbootldr,
- sysupdate,
- mountfsd,
- systemd-mount.

There may be several more places we should trigger automount, but let's
do that later.

Follow-up for c5de7b14ae.
Fixes #38565.
Replaces #38569.

Co-authored-by: Luca Boccassi <luca.boccassi@gmail.com>
This commit is contained in:
Yu Watanabe
2025-08-20 02:32:02 +09:00
co-authored by Luca Boccassi
parent 9dcd034085
commit 4279336618
16 changed files with 69 additions and 48 deletions
+4 -2
View File
@@ -21,6 +21,7 @@
#define CHASE_NO_SHORTCUT_MASK \
(CHASE_NONEXISTENT | \
CHASE_NO_AUTOFS | \
CHASE_TRIGGER_AUTOFS | \
CHASE_SAFE | \
CHASE_STEP | \
CHASE_PROHIBIT_SYMLINKS | \
@@ -92,7 +93,7 @@ static int openat_opath_with_automount(int dir_fd, const char *path, bool automo
/* 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
* automounts, but we may want that when CHASE_TRIGGER_AUTOFS is set. 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.
*
@@ -133,6 +134,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT));
assert(!FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY|CHASE_MUST_BE_REGULAR));
assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME));
assert(!FLAGS_SET(flags, CHASE_NO_AUTOFS|CHASE_TRIGGER_AUTOFS));
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
if (FLAGS_SET(flags, CHASE_STEP))
@@ -401,7 +403,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
}
/* 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));
child = r = openat_opath_with_automount(fd, first, /* automount = */ FLAGS_SET(flags, CHASE_TRIGGER_AUTOFS));
if (r < 0) {
if (r != -ENOENT)
return r;
+13 -12
View File
@@ -7,27 +7,28 @@ typedef enum ChaseFlags {
CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */
CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
CHASE_TRIGGER_AUTOFS = 1 << 3, /* Use open_tree() rather than openat() to trigger autofs mount */
CHASE_SAFE = 1 << 4, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
CHASE_TRAIL_SLASH = 1 << 5, /* Any trailing slash will be preserved */
CHASE_STEP = 1 << 6, /* Just execute a single step of the normalization */
CHASE_NOFOLLOW = 1 << 7, /* Do not follow the path's right-most component. With ret_fd, when the path's
* right-most component refers to symlink, return O_PATH fd of the symlink. */
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered.
CHASE_WARN = 1 << 8, /* Emit an appropriate warning when an error is encountered.
* Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
CHASE_AT_RESOLVE_IN_ROOT = 1 << 9, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
* relative to the given directory fd instead of root. */
CHASE_PROHIBIT_SYMLINKS = 1 << 9, /* Refuse all symlinks */
CHASE_PARENT = 1 << 10, /* Chase the parent directory of the given path. Note that the
CHASE_PROHIBIT_SYMLINKS = 1 << 10, /* Refuse all symlinks */
CHASE_PARENT = 1 << 11, /* Chase the parent directory of the given path. Note that the
* full path is still stored in ret_path and only the returned
* file descriptor will point to the parent directory. Note that
* the result path is the root or '.', then the file descriptor
* also points to the result path even if this flag is set.
* When this specified, chase() will succeed with 1 even if the
* file points to the last path component does not exist. */
CHASE_MKDIR_0755 = 1 << 11, /* Create any missing directories in the given path. */
CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved path */
CHASE_MUST_BE_DIRECTORY = 1 << 13, /* Fail if returned inode fd is not a dir */
CHASE_MUST_BE_REGULAR = 1 << 14, /* Fail if returned inode fd is not a regular file */
CHASE_MKDIR_0755 = 1 << 12, /* Create any missing directories in the given path. */
CHASE_EXTRACT_FILENAME = 1 << 13, /* Only return the last component of the resolved path */
CHASE_MUST_BE_DIRECTORY = 1 << 14, /* Fail if returned inode fd is not a dir */
CHASE_MUST_BE_REGULAR = 1 << 15, /* Fail if returned inode fd is not a regular file */
} ChaseFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);