Commit Graph
69 Commits
Author SHA1 Message Date
Yu Watanabe 542552612b chase: tweaks to chase_open()/chase_openat() (#38984) 2025-09-19 16:11:24 +09:00
Lennart Poettering 93dea63fab fileio: modernize xopendirat() a bit 2025-09-19 08:39:32 +02:00
Lennart Poettering fb7151a350 chase: honour CHASE_MUST_BE_DIRECTORY/CHASE_MUST_BE_REGULAR properly in chase_and_openat() and related calls 2025-09-18 09:36:44 +02:00
Lennart Poettering be1117f712 chase: mask away CHASE_MUST_BE_REGULAR in chase_openat()
We pin the parent directory of the specified directory via CHASE_PARENT,
but if we do that we really should mask off CHASE_MUST_BE_REGULAR,
because a parent dir of course is a dir, nothing else. The
CHASE_MUST_BE_REGULAR after all should apply to the file created in that
dir, not to the parent.
2025-09-18 09:36:44 +02:00
Yu WatanabeandLuca Boccassi 4279336618 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>
2025-08-20 02:32:02 +09:00
Yu Watanabe 9dcd034085 chase: introduce CHASE_NO_SHORTCUT_MASK 2025-08-20 02:24:39 +09:00
Yu Watanabe 2e7d7e929e Revert "chase: invert CHASE_NO_AUTOFS and only set it where needed"
This reverts commit 490aa05ca1.

As commented https://github.com/systemd/systemd/pull/38569#discussion_r2284978273,
the commit makes autofs check bypassed. Before the commit, when
CHASE_NO_AUTOFS is set, we did not shortcut chasing paths, and refused
any autofs mount points in the path. However, with the commit, the flag
was swapped but even when CHASE_AUTOFS is unset, the autofs check may be
skipped.

To fix the issue, rather than swapping the flag, we should introduce
another flag, say CHASE_TRIGGER_AUTOFS. This revert the commit, and in a
later commit, the new flag will be introduced.
2025-08-20 02:15:36 +09:00
Luca Boccassi 490aa05ca1 chase: invert CHASE_NO_AUTOFS and only set it where needed
Since https://github.com/systemd/systemd/commit/c5de7b14ae2e08d267d8d75bc88934ac6aa7dcd6
file searching implies a new mount api syscall by default,
to trigger automounts.

This is problematic in NSS plugins, as they are dlopen'ed inside
processes by glibc, for two reasons.

First of all, potentially searching on a networked filesystem
automount could lead to nasty surprises, such as the process
responsible for setting up the network filesystem trying to
search on that same filesystem.

More importantly, the new mount api syscall was never part of
the filesystem seccomp filter that we provide by default, and
given mounting/remounting/bind mounting is one of the possible
ways to bypass sandboxing it is very likely not allowed when
custom filters are used in sandboxed processes, if they don't
need to do these operations otherwise.
The filesystem seccomp mask we provide has been updated, however
this only takes effect on the next restart of a service. When
systemd is upgraded via a package upgrade, the new nss plugin is
installed and will be immediately dlopen'ed by glibc when needed,
without waiting for the process to restart, which means the existing
seccomp filter applies, causing the filter to trigger.
Given it's not really possible for any arbitrary program to
predict which NSS modules glibc will load, given programs do not
configure that and instead nsswitch is set up by the sysadmin,
it's impossible to handle at each process level. It's also not
possible to know when it will be triggered, given the plugin
is not linked in each binary tools like need-restart cannot
even pre-emptively restart services that may be affected.

This means in practice, upgrading from systemd << v258 to >= v258
requires a reboot to avoid either subtle or catastrophic system
failures.

By avoiding to trigger automounts in nss-systemd we can avoid
both issues.

userdb drop-ins are searched for in:

/etc/userdb/
/run/userdb/
/run/host/userdb/
/usr/local/lib/userdb/
/usr/lib/userdb/

none of which are supported as automounts anyway.

Note that this happens only when the userdbd service is not running,
as otherwise nss-systemd will go through the varlink IPC, rather than
doing the searches in-process.

So invert CHASE_NO_AUTOFS to CHASE_AUTOFS and set it in the places where
we do want to trigger automounts, like looking for the ESP.

Follow-up for https://github.com/systemd/systemd/commit/c5de7b14ae2e08d267d8d75bc88934ac6aa7dcd6
Fixes https://github.com/systemd/systemd/issues/38565
2025-08-19 16:48:13 +02:00
Yu Watanabe 1cabb6905b chase: check the result is a directory or regular file only when the resolved path exists
Otherwise, if it is called with CHASE_NONEXISTENT, when we call
stat_verify_directory()/_regular() the struct stat is for one of the
parent directory, rather than for the result path.

With this change, we can safely specify CHASE_MUST_BE_DIRECTORY/REGULAR
with CHASE_NONEXISTENT.

More importantly, chaseat() internally sets CHASE_MUST_BE_DIRECTORY when
the input path ends with "/", "/,", "/..". Hence, without this change,
we cannot specify CHASE_NONEXISTENT safely.

Follow-up for 90b9f7a07e.
2025-07-29 03:37:48 +09:00
Yu Watanabe 628f45f4a3 chase: allow to request O_PATH fd even with CHASE_NONEXISTENT 2025-07-11 10:39:55 +09:00
Mike Yuan 1320e56283 chase: introduce openat_opath_with_automount() helper
Follow-up for c5de7b14ae

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.
2025-07-05 12:18:55 +02:00
Lennart Poettering c5de7b14ae chase: when chasing paths, trigger automounts
As it turns out open() with O_PATH does *not* trigger autofs, you get a
reference to the autofs inode, if not triggered.

But there's a way out: open_tree() (when specified without
OPEN_TREE_CLONE) is actually fully equivalent to open() with O_PATH –
with the exception of one thing: it *does* trigger automounts.

Thanks for Christian Brauner for pointing me to this and saving my day.

Fixes: #33155
2025-07-03 22:18:56 +01:00
Yu Watanabe 8ce463639a path-util: move empty_or_root_to_null() from chase.c
And rename it to empty_or_root_harder_to_null(), as it also checks if
the input path effectively points to the root by calling path_is_root().
This also adds simple test cases for the function.
2025-06-30 03:45:29 +09:00
Daan De Meyer 0c15577abe basic + fundamental: Clean up includes
Split out of #37344.
2025-05-25 10:06:07 +02:00
Daan De Meyer d9ccf6b349 path-util: Reduce transitive includes 2025-05-07 22:04:46 +09:00
Daan De Meyer fda652117e fs-util: Reduce transitive includes 2025-05-07 22:04:46 +09:00
Zbigniew Jędrzejewski-Szmek 1ae9b0cfa8 basic/glyph-util: rename "special glyph" to just "glyph"
Admittedly, some of our glyphs _are_ special, e.g. "O=" for SPECIAL_GLYPH_TOUCH ;)
But we don't need this in the name. The very long names make some invocations
very wordy, e.g. special_glyph(SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY).
Also, I want to add GLYPH_SPACE, which is not special at all.
2025-03-15 14:40:39 +01:00
Lennart Poettering 3351c0a510 chase: move appending of trailing slash out of loop
let's move final processing of the filename out of the loop, and apply
it in all cases, uniformly, even if we are asked to only return the
final filename.
2025-01-20 11:35:04 +01:00
Lennart Poettering 90b9f7a07e chase: introduce flags that verify that chased inode is regular file or dir
This also implies the new CHASE_MUST_BE_DIRECTORY flag in case the
specified path ends in a slash. This makes the rules stricter, it means
we'll be closer to how this is handled in kernel: if a path ends in a
slash it can never refer to a non-directory.
2025-01-20 11:35:03 +01:00
Lennart Poettering b0afd29f7d chase: use streq() not path_equal() to compare filenames 2025-01-20 11:31:55 +01:00
Lennart Poettering e49e76d64d chase: allow using chase() as mkdir_p() replacement
This allows using CHASE_MKDIR_0755 without CHASE_NONEXISTENT or
CHASE_PARENT, so that it will create the final component of the path
too should it be missing.

This is really useful as a mkdir_p() replacement that returns an fd to
the final component, and knows how to operate relative to a root fs.

Kinda reverts 4ea0bcb922 (which only
refused the flags combination which didn't work, instead of making it
work, which is what this commit does.)

This also corrects behaviour if CHASE_MKDIR_0755 is used in one more
way: we'll now always open the dir as O_PATH. This is generally the
better idea, but matters in particular once with allow using
CHASE_MKDIR_0755 to create the final component: we should uniformly
return an O_PATH dir that must be converted to a proper fd first before
using it.
2025-01-20 11:31:55 +01:00
Lennart Poettering 8beb8c3e35 fs-util: teach xopenat_full() to pick automatically if given as MODE_INVALID 2025-01-03 12:58:43 +01:00
Daan De Meyer 87333bd1dc chase: Fix shortcut
We can't shortcut chaseat() if CHASE_PARENT is set.
2024-10-04 21:34:33 +02:00
Daan De Meyer 7efaab482a chase: Tighten "." and "./" check
Currently the check also succeeds if the input path starts with a dot, whereas
we only want it to succeed for "." and "./". Tighten the check and add a test.
2024-06-11 13:55:35 +01:00
Zbigniew Jędrzejewski-Szmek 454318d3e8 various: use strdup_to() in various obvious cases
strdup_to() returns 0 on success and here we convert obvious blocks
which either return -ENOMEM or 0.
2024-03-20 15:13:44 +01:00
Yu Watanabe e40b11be04 fs-util: rename xopenat() -> xopanat_full() 2024-02-16 00:25:37 +09:00
Yu Watanabe bec1383623 chase: do not wrap xopenat() with RET_NERRNO()
Follow-up for 47f0e1b5e0.
2024-02-16 00:25:37 +09:00
Lennart Poettering 9c21cfdd7d chase: fix corner case when using CHASE_PARENT with a path ending in ".."
If we use CHASE_PARENT on a path ending in ".." then things are a bit
weird, because we the last path we look at is actually the *parent* and not
the *child* of the preceeding path. Hence we cannot just return the 2nd
to last fd we look at. We have to correct it, by going *two* levels up,
to get to the actual parent, and make sure CHASE_PARENT does what it
should.

Example: for the path /a/b/c chase() with CHASE_PARENT will return
/a/b/c as path, and the fd returned points to /a/b. All good.  But now,
for the path /a/b/c/.. chase() with CHASE_PARENT would previously return
/a/b as path (which is OK) but the fd would point to /a/b/c, which is
*not* the parent of /a/b, after all! To get to the actual parent of
/a/b we have to go *two* levels up to get to /a.

Very confusing. But that's what we here for, no?

@mrc0mmand ran into this in https://github.com/systemd/systemd/pull/28891#issuecomment-1782833722
2023-11-01 14:43:24 +00:00
Yu Watanabe 4445242a1e chase: propagate error in dir_fd_is_root()
Otherwise, if it fails, chaseat() may return unexpected result and
triggers an assertion in chase().
2023-07-29 22:09:32 +09:00
Yu Watanabe 83c57d8cde chase: check root path in more detail
In chaseat() we call dir_fd_is_root() several places, and the final
result depends on it. If the root path specified to `chase()` is not
normalized but points to "/", e.g. "/../", assertions in `chaseat()` or
`chase()` may be triggered.
2023-07-29 22:09:28 +09:00
Yu Watanabe 4de5b4e375 chase: carefully handle result of extracting parent directory
Should not change any behavior.
2023-07-29 21:58:30 +09:00
Yu Watanabe 1c13bdf342 chase: add two more assertions
When 'need_absolute' is true, 'done' should always contain "/" at the
beginning, and thus should not be NULL.
2023-07-29 21:58:30 +09:00
Yu Watanabe b7e957d30f chase: drop CHASE_AT_RESOLVE_IN_ROOT earlier
The flag will be anyway dropped in chaseat(), but let's shortcut.
2023-07-29 21:58:29 +09:00
Yu Watanabe 5f0bae7bff chase: fix CHASE_STEP with ".." 2023-07-29 21:58:29 +09:00
Yu Watanabe 00a050b395 chase: reuse "done" to open fd of starting point
For readability that 'done' and 'fd' are always consistent with each other.

- dir_fd == AT_FDCWD:
  - path is absolute:
    - previous: fd = open("/")
    - current:  fd = openat(AT_FDCWD, "/")
  - path is relative:
    - previous: fd = openat(AT_FDCWD, ".")
    - current:  fd = openat(AT_FDCWD, ".")
- dir_fd >= 0:
  - dir_fd points to "/":
    - previous: fd = openat(dir_fd, ".")
    - current:  fd = openat(dir_fd, "/")
  - dir_fd does not point to "/":
    - previous: fd = openat(dir_fd, ".")
    - current:  fd = openat(dir_fd, ".")

Hence, this should not change any behavior. Just refactoring.
2023-07-29 21:58:29 +09:00
Zbigniew Jędrzejewski-Szmek d1579f0b70 Merge pull request #27885 from DaanDeMeyer/please-dont-make-me-write-more-openat-helpers
Add more openat like helper functions
2023-06-07 13:25:33 +02:00
Daan De Meyer 0bb5653530 Merge pull request #27918 from yuwata/chase-filename
chase: handle root path more carefully in chase_and_open()
2023-06-06 15:01:37 +02:00
Daan De Meyer 3c8f449cc8 chase: Allow passing NULL as the empty path to chaseat()
Per coding style, we should accept NULL as the empty path.
2023-06-06 14:42:03 +02:00
Yu Watanabe 308b189511 chase: fix triggering assertion 2023-06-05 10:22:47 +02:00
Yu Watanabe 3991f35fbd chase: handle root path more carefully in chase_and_open()
chase_and_open() may be called with relative root path.
2023-06-05 15:50:46 +09:00
Daan De Meyer 420d2e3136 fs-util: Add XOpenFlags with XO_LABEL flag to have xopenat() MAC label files/dirs 2023-05-31 13:15:56 +02:00
Yu Watanabe 60e761d8f3 chase: replace path_prefix_root_cwd() with chaseat_prefix_root()
The function path_prefix_root_cwd() was introduced for prefixing the
result from chaseat() with root, but
- it is named slightly generic,
- the logic is different from what chase() does.

This makes the name more explanative and specific for the result of the
chaseat(), and make the logic consistent with chase().

Fixes https://github.com/systemd/systemd/pull/27199#issuecomment-1511387731.

Follow-up for #27199.
2023-04-19 03:38:59 +09:00
Yu Watanabe 9a0dcf03fa chase: prefix with the root directory only when it is not "/" 2023-04-19 03:28:34 +09:00
Yu Watanabe 237bf933de chase: drop repeated call of empty_to_root() 2023-04-19 03:28:34 +09:00
Yu Watanabe b3ef56bc8e chase: update outdated comment about result path 2023-04-19 03:28:34 +09:00
Yu Watanabe 24be89ebd8 chase: make the result absolute when a symlink is absolute
As the path may be outside of the specified dir_fd.
2023-04-19 03:28:34 +09:00
Yu Watanabe c0552b359c chase: make chaseat() provides absolute path also when dir_fd points to the root directory
Usually, we pass the file descriptor of the root directory to chaseat()
when `--root=` is not specified. Previously, even in such case, the
result was relative, and we need to prefix the path with "/" when we
want to pass the path to other functions that do not support dir_fd, or
log or show the path. That's inconvenient.
2023-04-19 03:28:34 +09:00
Yu Watanabe 4ea0bcb922 chase: CHASE_MKDIR_0755 requires CHASE_NONEXISTENT and/or CHASE_PARENT
When CHASE_MKDIR_0755 is specified without CHASE_NONEXISTENT and
CHASE_PARENT, then chase() succeeds only when the file specified by
the path already exists, and in that case, chase() does not create
any parent directories, and CHASE_MKDIR_0755 is meaningless.

Let's mention that CHASE_MKDIR_0755 needs to be specified with
CHASE_NONEXISTENT or CHASE_PARENT, and adds a assertion about that.
2023-04-14 16:36:13 +09:00
Yu Watanabe 5a2f674a00 chase: use FLAGS_SET() macro 2023-04-14 16:28:54 +09:00
Yu Watanabe 0d68cd72b6 chase: drop redundant call of delete_trailing_chars()
In that branch, 'root' is a non-root and absolute path.
Hence, delete_trailing_chars() does not make the path empty.
And, if the path contains redundant slashes at the end, that will be
dropped by path_simplify().
2023-04-11 09:06:05 +02:00