Introduce ERRNO_IS_FS_WRITE_REFUSED(), and use it in binfmt_mounted() (#38117)

- This introduces ERRNO_IS_FS_WRITE_REFUSED(), and apply it where
usable.
- This makes unexpected errors in access_fd() called by binfmt_mounted()
propagated to the caller.
- Renames binfmt_mounted() to binfmt_mounted_and_writable(), as it also
checks the fs is writable.
- Voidifies one disable_binfmt() call in shutdown.c.
This commit is contained in:
Lennart Poettering
2025-07-10 21:38:13 +02:00
committed by GitHub
16 changed files with 43 additions and 32 deletions

View File

@@ -188,6 +188,12 @@ static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
}
_DEFINE_ABS_WRAPPER(PRIVILEGE);
/* Three different errors for writing on a filesystem */
static inline bool ERRNO_IS_NEG_FS_WRITE_REFUSED(intmax_t r) {
return r == -EROFS || ERRNO_IS_NEG_PRIVILEGE(r);
}
_DEFINE_ABS_WRAPPER(FS_WRITE_REFUSED);
/* Three different errors for "not enough disk space" */
static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
return IN_SET(r,

View File

@@ -193,10 +193,10 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
static int binfmt_mounted_warn(void) {
static int binfmt_mounted_and_writable_warn(void) {
int r;
r = binfmt_mounted();
r = binfmt_mounted_and_writable();
if (r < 0)
return log_error_errno(r, "Failed to check if /proc/sys/fs/binfmt_misc is mounted: %m");
if (r == 0)
@@ -222,7 +222,7 @@ static int run(int argc, char *argv[]) {
return disable_binfmt();
if (argc > optind) {
r = binfmt_mounted_warn();
r = binfmt_mounted_and_writable_warn();
if (r <= 0)
return r;
@@ -239,7 +239,7 @@ static int run(int argc, char *argv[]) {
if (arg_cat_flags != CAT_CONFIG_OFF)
return cat_config(files);
r = binfmt_mounted_warn();
r = binfmt_mounted_and_writable_warn();
if (r <= 0)
return r;

View File

@@ -62,7 +62,7 @@
/* Returns the log level to use when cgroup attribute writes fail. When an attribute is missing or we have access
* problems we downgrade to LOG_DEBUG. This is supposed to be nice to container managers and kernels which want to mask
* out specific attributes from us. */
#define LOG_LEVEL_CGROUP_WRITE(r) (IN_SET(ABS(r), ENOENT, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING)
#define LOG_LEVEL_CGROUP_WRITE(r) ((ABS(r) == ENOENT || ERRNO_IS_FS_WRITE_REFUSED(r)) ? LOG_DEBUG : LOG_WARNING)
static void unit_remove_from_cgroup_empty_queue(Unit *u);

View File

@@ -1271,8 +1271,8 @@ static void bump_file_max_and_nr_open(void) {
* different, but the operation would fail silently.) */
r = sysctl_write("fs/file-max", LONG_MAX_STR);
if (r < 0)
log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING,
r, "Failed to bump fs.file-max, ignoring: %m");
log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to bump fs.file-max, ignoring: %m");
#endif
#if BUMP_PROC_SYS_FS_NR_OPEN
@@ -1293,7 +1293,8 @@ static void bump_file_max_and_nr_open(void) {
continue;
}
if (r < 0) {
log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.nr_open, ignoring: %m");
log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to bump fs.nr_open, ignoring: %m");
break;
}
@@ -1534,7 +1535,7 @@ static int bump_unix_max_dgram_qlen(void) {
r = sysctl_write("net/unix/max_dgram_qlen", STRINGIFY(DEFAULT_UNIX_MAX_DGRAM_QLEN));
if (r < 0)
return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
return log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to bump AF_UNIX datagram queue length, ignoring: %m");
return 1;

View File

@@ -1269,7 +1269,7 @@ static void mount_enter_mounting(Mount *m) {
* when the path is on NFS. See issue #24120. All such errors will be logged in the debug level. */
if (r < 0 && r != -EEXIST)
log_unit_full_errno(UNIT(m),
(r == -EROFS || ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_WARNING,
ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING,
r, "Failed to make bind mount source '%s', ignoring: %m", p->what);
}

View File

@@ -1907,14 +1907,14 @@ static int setup_timezone(const char *dest) {
log_debug_errno(r, "Timezone %s does not exist (or is not accessible) in container, not creating symlink: %m", z);
else {
if (unlink(where) < 0 && errno != ENOENT) {
log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING, /* Don't complain on read-only images */
log_full_errno(ERRNO_IS_FS_WRITE_REFUSED(errno) ? LOG_DEBUG : LOG_WARNING, /* Don't complain on read-only images */
errno, "Failed to remove existing timezone info %s in container, ignoring: %m", where);
return 0;
}
what = strjoina("../usr/share/zoneinfo/", z);
if (symlink(what, where) < 0) {
log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING,
log_full_errno(ERRNO_IS_FS_WRITE_REFUSED(errno) ? LOG_DEBUG : LOG_WARNING,
errno, "Failed to correct timezone of container, ignoring: %m");
return 0;
}
@@ -1949,7 +1949,7 @@ static int setup_timezone(const char *dest) {
/* If mounting failed, try to copy */
r = copy_file_atomic("/etc/localtime", where, 0644, COPY_REFLINK|COPY_REPLACE);
if (r < 0) {
log_full_errno(IN_SET(r, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to copy /etc/localtime to %s, ignoring: %m", where);
return 0;
}
@@ -2085,7 +2085,7 @@ static int setup_resolv_conf(const char *dest) {
* If the disk image is read-only, there's also no point in complaining.
*/
log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC, RESOLV_CONF_COPY_UPLINK, RESOLV_CONF_COPY_STUB) &&
IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
(r == -ELOOP || ERRNO_IS_NEG_FS_WRITE_REFUSED(r)) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to copy /etc/resolv.conf to %s, ignoring: %m", where);
return 0;
}

View File

@@ -11,7 +11,7 @@
#include "missing_magic.h"
#include "stat-util.h"
int binfmt_mounted(void) {
int binfmt_mounted_and_writable(void) {
_cleanup_close_ int fd = -EBADF;
int r;
@@ -25,7 +25,13 @@ int binfmt_mounted(void) {
if (r <= 0)
return r;
return access_fd(fd, W_OK) >= 0;
r = access_fd(fd, W_OK);
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
return false;
if (r < 0)
return r;
return true;
}
int disable_binfmt(void) {
@@ -37,7 +43,7 @@ int disable_binfmt(void) {
* We are a bit careful here, since binfmt_misc might still be an autofs which we don't want to
* trigger. */
r = binfmt_mounted();
r = binfmt_mounted_and_writable();
if (r < 0)
return log_warning_errno(r, "Failed to determine whether binfmt_misc is mounted: %m");
if (r == 0) {

View File

@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
int binfmt_mounted(void);
int binfmt_mounted_and_writable(void);
int disable_binfmt(void);

View File

@@ -660,7 +660,7 @@ int loop_device_make_by_path_at(
r = fd;
/* Retry read-only? */
if (open_flags >= 0 || !(ERRNO_IS_PRIVILEGE(r) || r == -EROFS))
if (open_flags >= 0 || !ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
return r;
fd = xopenat(dir_fd, path, basic_flags|direct_flags|O_RDONLY);

View File

@@ -447,7 +447,7 @@ int main(int argc, char *argv[]) {
(void) sync_with_progress(-EBADF);
disable_coredumps();
disable_binfmt();
(void) disable_binfmt();
log_info("Sending SIGTERM to remaining processes...");
broadcast_signal(SIGTERM, true, true, arg_timeout);

View File

@@ -321,7 +321,7 @@ static int add_export_unix_socket(
log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there.");
return 0;
}
if (errno == EROFS || ERRNO_IS_PRIVILEGE(errno)) {
if (ERRNO_IS_FS_WRITE_REFUSED(errno)) {
log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there.");
return 0;
}

View File

@@ -95,7 +95,7 @@ static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_
* permission problem here, since that's how container managers usually protected their
* sysctls.)
* In all other cases log an error and make the tool fail. */
if (ignore_failure || (!arg_strict && (r == -EROFS || ERRNO_IS_PRIVILEGE(r))))
if (ignore_failure || (!arg_strict && ERRNO_IS_NEG_FS_WRITE_REFUSED(r)))
log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
else if (ignore_enoent && r == -ENOENT)
log_warning_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);

View File

@@ -3,8 +3,8 @@
#include "binfmt-util.h"
#include "tests.h"
TEST(binfmt_mounted) {
ASSERT_OK(binfmt_mounted());
TEST(binfmt_mounted_and_writable) {
ASSERT_OK(binfmt_mounted_and_writable());
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@@ -64,10 +64,8 @@ TEST(cg_create) {
(void) cg_trim(test_b, /* delete_root= */ true);
r = cg_create(test_a);
if (IN_SET(r, -EPERM, -EACCES, -EROFS)) {
log_info_errno(r, "Skipping %s: %m", __func__);
return;
}
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
return (void) log_tests_skipped_errno(r, "%s: Failed to create cgroup %s", __func__, test_a);
ASSERT_OK_EQ(r, 1);
ASSERT_OK_ZERO(cg_create(test_a));

View File

@@ -51,7 +51,7 @@ int main(int argc, char *argv[]) {
log_info("Reducing limit by one to %"PRIu64"", limit-1);
r = procfs_tasks_set_limit(limit-1);
if (IN_SET(r, -ENOENT, -EROFS) || ERRNO_IS_PRIVILEGE(r))
if (r == -ENOENT || ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
return log_tests_skipped_errno(r, "can't set tasks limit");
assert_se(r >= 0);

View File

@@ -54,14 +54,14 @@ TEST(sysctl_read) {
assert_se(STR_IN_SET(s, "0", "1"));
r = sysctl_write_ip_property(AF_INET, "lo", "forwarding", s, NULL);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS);
assert_se(r >= 0 || ERRNO_IS_NEG_FS_WRITE_REFUSED(r));
s = mfree(s);
assert_se(sysctl_read_ip_property(AF_INET, NULL, "ip_forward", &s));
assert_se(STR_IN_SET(s, "0", "1"));
r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", s, NULL);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS);
assert_se(r >= 0 || ERRNO_IS_NEG_FS_WRITE_REFUSED(r));
s = mfree(s);
assert_se(sysctl_read("kernel/hostname", &s) >= 0);
@@ -69,7 +69,7 @@ TEST(sysctl_read) {
ASSERT_STREQ(s, u.nodename);
r = sysctl_write("kernel/hostname", s);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS);
assert_se(r >= 0 || ERRNO_IS_NEG_FS_WRITE_REFUSED(r));
}
DEFINE_TEST_MAIN(LOG_INFO);