summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-25 02:54:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-25 02:54:52 +0000
commit51fac37bb20c9440a9a4e0a20846c139364d6d13 (patch)
tree77c11a0dffc2c15542689f3a51d12d5076c477e8 /src
parentAdding upstream version 255.4. (diff)
downloadsystemd-51fac37bb20c9440a9a4e0a20846c139364d6d13.tar.xz
systemd-51fac37bb20c9440a9a4e0a20846c139364d6d13.zip
Adding upstream version 255.5.upstream/255.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze-srk.c4
-rw-r--r--src/backlight/backlight.c4
-rw-r--r--src/basic/chase.c30
-rw-r--r--src/basic/chattr-util.c2
-rw-r--r--src/basic/env-util.c31
-rw-r--r--src/basic/env-util.h2
-rw-r--r--src/basic/filesystems-gperf.gperf1
-rw-r--r--src/basic/fs-util.c8
-rw-r--r--src/basic/fs-util.h10
-rw-r--r--src/basic/hashmap.h2
-rw-r--r--src/basic/lock-util.c14
-rw-r--r--src/basic/log.c10
-rw-r--r--src/basic/meson.build2
-rw-r--r--src/basic/missing_magic.h5
-rw-r--r--src/basic/os-util.c55
-rw-r--r--src/basic/os-util.h1
-rw-r--r--src/basic/stat-util.c26
-rw-r--r--src/basic/stat-util.h3
-rw-r--r--src/basic/terminal-util.c2
-rw-r--r--src/basic/terminal-util.h3
-rw-r--r--src/basic/virt.c1
-rw-r--r--src/boot/efi/boot.c6
-rw-r--r--src/boot/efi/cpio.c2
-rw-r--r--src/boot/efi/meson.build5
-rw-r--r--src/boot/efi/stub.c8
-rw-r--r--src/busctl/busctl.c9
-rw-r--r--src/core/bpf-socket-bind.c9
-rw-r--r--src/core/bpf/socket_bind/socket-bind-api.bpf.h7
-rw-r--r--src/core/bpf/socket_bind/socket-bind.bpf.c3
-rw-r--r--src/core/dynamic-user.c4
-rw-r--r--src/core/exec-invoke.c4
-rw-r--r--src/core/main.c2
-rw-r--r--src/core/manager-serialize.c3
-rw-r--r--src/core/mount.c89
-rw-r--r--src/core/scope.c2
-rw-r--r--src/core/service.c18
-rw-r--r--src/core/show-status.c15
-rw-r--r--src/core/socket.c5
-rw-r--r--src/core/swap.c4
-rw-r--r--src/cryptenroll/cryptenroll-tpm2.c4
-rw-r--r--src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c4
-rw-r--r--src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h2
-rw-r--r--src/cryptsetup/cryptsetup-tpm2.c4
-rw-r--r--src/cryptsetup/cryptsetup.c4
-rw-r--r--src/dissect/dissect.c3
-rw-r--r--src/home/homed-manager.c2
-rw-r--r--src/home/homework-cifs.c62
-rw-r--r--src/journal-remote/journal-gatewayd.c1
-rw-r--r--src/journal-remote/journal-remote-main.c22
-rw-r--r--src/journal-remote/journal-remote.c6
-rw-r--r--src/journal-remote/journal-remote.h2
-rw-r--r--src/journal-remote/journal-upload.c40
-rw-r--r--src/journal-remote/journal-upload.h1
-rw-r--r--src/journal/cat.c10
-rw-r--r--src/journal/journalctl.c14
-rwxr-xr-xsrc/kernel-install/60-ukify.install.in6
-rwxr-xr-xsrc/kernel-install/90-uki-copy.install4
-rw-r--r--src/libsystemd-network/dhcp-option.c55
-rw-r--r--src/libsystemd-network/dhcp-option.h1
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c10
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c14
-rw-r--r--src/libsystemd/sd-bus/bus-error.c6
-rw-r--r--src/libsystemd/sd-device/device-private.h5
-rw-r--r--src/libsystemd/sd-device/sd-device.c4
-rw-r--r--src/libsystemd/sd-event/sd-event.c19
-rw-r--r--src/libsystemd/sd-event/test-event.c18
-rw-r--r--src/libsystemd/sd-id128/id128-util.c4
-rw-r--r--src/libsystemd/sd-journal/journal-file.c11
-rw-r--r--src/libsystemd/sd-journal/journal-verify.c2
-rw-r--r--src/libsystemd/sd-journal/sd-journal.c2
-rw-r--r--src/login/logind-dbus.c2
-rw-r--r--src/network/networkd-state-file.c4
-rw-r--r--src/network/tc/qdisc.c6
-rw-r--r--src/network/tc/tclass.c6
-rw-r--r--src/partition/repart.c4
-rw-r--r--src/pcrextend/pcrextend.c2
-rw-r--r--src/pcrlock/pcrlock.c8
-rw-r--r--src/portable/portable.c88
-rw-r--r--src/resolve/resolved-bus.c10
-rw-r--r--src/resolve/resolved-dns-cache.c16
-rw-r--r--src/resolve/resolved-dns-query.c27
-rw-r--r--src/resolve/resolved-dns-rr.c17
-rw-r--r--src/resolve/resolved-dns-rr.h1
-rw-r--r--src/resolve/resolved-dns-scope.c61
-rw-r--r--src/resolve/resolved-dns-scope.h1
-rw-r--r--src/resolve/resolved-dns-stream.c41
-rw-r--r--src/resolve/resolved-dns-stream.h1
-rw-r--r--src/resolve/resolved-dns-stub.c10
-rw-r--r--src/resolve/resolved-dns-synthesize.c2
-rw-r--r--src/resolve/resolved-dns-transaction.c193
-rw-r--r--src/resolve/resolved-dns-transaction.h5
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c5
-rw-r--r--src/rpm/macros.systemd.in1
-rw-r--r--src/shared/base-filesystem.c4
-rw-r--r--src/shared/blockdev-util.c56
-rw-r--r--src/shared/bpf-dlopen.c11
-rw-r--r--src/shared/btrfs-util.c10
-rw-r--r--src/shared/copy.c123
-rw-r--r--src/shared/copy.h1
-rw-r--r--src/shared/creds-util.c4
-rw-r--r--src/shared/data-fd-util.h2
-rw-r--r--src/shared/dlfcn-util.c2
-rw-r--r--src/shared/hibernate-util.c8
-rw-r--r--src/shared/idn-util.c5
-rw-r--r--src/shared/install.c7
-rw-r--r--src/shared/journal-file-util.c15
-rw-r--r--src/shared/logs-show.c3
-rw-r--r--src/shared/loop-util.c8
-rw-r--r--src/shared/open-file.c2
-rw-r--r--src/shared/serialize.c55
-rw-r--r--src/shared/tpm2-util.c40
-rw-r--r--src/shared/tpm2-util.h1
-rw-r--r--src/shared/verbs.c29
-rw-r--r--src/shared/watchdog.c16
-rw-r--r--src/systemctl/systemctl-logind.c2
-rw-r--r--src/systemctl/systemctl-show.c2
-rw-r--r--src/systemd/sd-bus-vtable.h8
-rw-r--r--src/test/test-btrfs.c2
-rw-r--r--src/test/test-copy.c26
-rw-r--r--src/test/test-fs-util.c46
-rw-r--r--src/test/test-open-file.c10
-rw-r--r--src/test/test-stat-util.c19
-rw-r--r--src/tmpfiles/tmpfiles.c97
-rw-r--r--src/tpm2-setup/tpm2-setup.c17
-rwxr-xr-xsrc/ukify/ukify.py61
-rw-r--r--src/userdb/userdbctl.c6
-rw-r--r--src/userdb/userdbd-manager.c8
127 files changed, 1293 insertions, 677 deletions
diff --git a/src/analyze/analyze-srk.c b/src/analyze/analyze-srk.c
index 0e24b41..6faf2c2 100644
--- a/src/analyze/analyze-srk.c
+++ b/src/analyze/analyze-srk.c
@@ -11,9 +11,9 @@ int verb_srk(int argc, char *argv[], void *userdata) {
_cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
int r;
- r = tpm2_context_new(/* device= */ NULL, &c);
+ r = tpm2_context_new_or_warn(/* device= */ NULL, &c);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
r = tpm2_get_srk(
c,
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c
index 5ac9f90..b2032ad 100644
--- a/src/backlight/backlight.c
+++ b/src/backlight/backlight.c
@@ -55,6 +55,10 @@ static int has_multiple_graphics_cards(void) {
if (r < 0)
return r;
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
if (r < 0)
return r;
diff --git a/src/basic/chase.c b/src/basic/chase.c
index 26bc2d6..9f5477e 100644
--- a/src/basic/chase.c
+++ b/src/basic/chase.c
@@ -374,11 +374,11 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
return r;
if (FLAGS_SET(flags, CHASE_MKDIR_0755) && !isempty(todo)) {
- child = xopenat(fd,
- first,
- O_DIRECTORY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC,
- /* xopen_flags = */ 0,
- 0755);
+ child = xopenat_full(fd,
+ first,
+ O_DIRECTORY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC,
+ /* xopen_flags = */ 0,
+ 0755);
if (child < 0)
return child;
} else if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo)) {
@@ -760,10 +760,10 @@ int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, i
if (empty_or_root(root) && !ret_path &&
(chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0)
/* Shortcut this call if none of the special features of this call are requested */
- return xopenat(AT_FDCWD, path,
- open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
- /* xopen_flags = */ 0,
- mode);
+ return xopenat_full(AT_FDCWD, path,
+ open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
+ /* xopen_flags = */ 0,
+ mode);
r = chase(path, root, CHASE_PARENT|chase_flags, &p, &path_fd);
if (r < 0)
@@ -777,7 +777,7 @@ int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, i
return r;
}
- r = xopenat(path_fd, strempty(fname), open_flags|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
+ r = xopenat_full(path_fd, strempty(fname), open_flags|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
if (r < 0)
return r;
@@ -964,10 +964,10 @@ int chase_and_openat(int dir_fd, const char *path, ChaseFlags chase_flags, int o
if (dir_fd == AT_FDCWD && !ret_path &&
(chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0)
/* Shortcut this call if none of the special features of this call are requested */
- return xopenat(dir_fd, path,
- open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
- /* xopen_flags = */ 0,
- mode);
+ return xopenat_full(dir_fd, path,
+ open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
+ /* xopen_flags = */ 0,
+ mode);
r = chaseat(dir_fd, path, chase_flags|CHASE_PARENT, &p, &path_fd);
if (r < 0)
@@ -979,7 +979,7 @@ int chase_and_openat(int dir_fd, const char *path, ChaseFlags chase_flags, int o
return r;
}
- r = xopenat(path_fd, strempty(fname), open_flags|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
+ r = xopenat_full(path_fd, strempty(fname), open_flags|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
if (r < 0)
return r;
diff --git a/src/basic/chattr-util.c b/src/basic/chattr-util.c
index fe8b9ab..d76be5c 100644
--- a/src/basic/chattr-util.c
+++ b/src/basic/chattr-util.c
@@ -29,7 +29,7 @@ int chattr_full(
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd < 0)
return fd;
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index d3bf733..a97651d 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -983,8 +983,8 @@ int putenv_dup(const char *assignment, bool override) {
}
int setenv_systemd_exec_pid(bool update_only) {
- char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
+ int r;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
@@ -995,10 +995,9 @@ int setenv_systemd_exec_pid(bool update_only) {
if (streq_ptr(e, "*"))
return 0;
- xsprintf(str, PID_FMT, getpid_cached());
-
- if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
- return -errno;
+ r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
+ if (r < 0)
+ return r;
return 1;
}
@@ -1093,3 +1092,25 @@ int set_full_environment(char **env) {
return 0;
}
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ int r;
+
+ assert(name);
+
+ if (!valuef)
+ return RET_NERRNO(unsetenv(name));
+
+ va_start(ap, valuef);
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ r = vasprintf(&value, valuef, ap);
+ REENABLE_WARNING;
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return RET_NERRNO(setenv(name, value, overwrite));
+}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index f7fb1e9..34cf1f9 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -79,3 +79,5 @@ int getenv_path_list(const char *name, char ***ret_paths);
int getenv_steal_erase(const char *name, char **ret);
int set_full_environment(char **env);
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
diff --git a/src/basic/filesystems-gperf.gperf b/src/basic/filesystems-gperf.gperf
index e8c5357..1cd66b5 100644
--- a/src/basic/filesystems-gperf.gperf
+++ b/src/basic/filesystems-gperf.gperf
@@ -91,6 +91,7 @@ ocfs2, {OCFS2_SUPER_MAGIC}
openpromfs, {OPENPROM_SUPER_MAGIC}
orangefs, {ORANGEFS_DEVREQ_MAGIC}
overlay, {OVERLAYFS_SUPER_MAGIC}
+pidfs, {PID_FS_MAGIC}
pipefs, {PIPEFS_MAGIC}
ppc-cmm, {PPC_CMM_MAGIC}
proc, {PROC_SUPER_MAGIC}
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 9ba9268..5bc7d2f 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -1054,7 +1054,7 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
path = fname;
}
- fd = xopenat(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
+ fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
if (IN_SET(fd, -ELOOP, -ENOTDIR))
return -EEXIST;
if (fd < 0)
@@ -1110,7 +1110,7 @@ int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, b
}
}
-int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
+int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF;
bool made = false;
int r;
@@ -1191,7 +1191,7 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
return TAKE_FD(fd);
}
-int xopenat_lock(
+int xopenat_lock_full(
int dir_fd,
const char *path,
int open_flags,
@@ -1214,7 +1214,7 @@ int xopenat_lock(
for (;;) {
struct stat st;
- fd = xopenat(dir_fd, path, open_flags, xopen_flags, mode);
+ fd = xopenat_full(dir_fd, path, open_flags, xopen_flags, mode);
if (fd < 0)
return fd;
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 1023ab7..6a1e2e7 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -137,6 +137,12 @@ typedef enum XOpenFlags {
XO_SUBVOLUME = 1 << 1,
} XOpenFlags;
-int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
+int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
+static inline int xopenat(int dir_fd, const char *path, int open_flags) {
+ return xopenat_full(dir_fd, path, open_flags, 0, 0);
+}
-int xopenat_lock(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode, LockType locktype, int operation);
+int xopenat_lock_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode, LockType locktype, int operation);
+static inline int xopenat_lock(int dir_fd, const char *path, int open_flags, LockType locktype, int operation) {
+ return xopenat_lock_full(dir_fd, path, open_flags, 0, 0, locktype, operation);
+}
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
index 233f1d7..d0ebdf5 100644
--- a/src/basic/hashmap.h
+++ b/src/basic/hashmap.h
@@ -39,8 +39,8 @@ typedef struct IteratedCache IteratedCache; /* Caches the iterated order of on
* by hashmap users, so the definition has to be here. Do not use its fields
* directly. */
typedef struct {
- unsigned idx; /* index of an entry to be iterated next */
const void *next_key; /* expected value of that entry's key pointer */
+ unsigned idx; /* index of an entry to be iterated next */
#if ENABLE_DEBUG_HASHMAP
unsigned put_count; /* hashmap's put_count recorded at start of iteration */
unsigned rem_count; /* hashmap's rem_count in previous iteration */
diff --git a/src/basic/lock-util.c b/src/basic/lock-util.c
index 047fd01..7bffe85 100644
--- a/src/basic/lock-util.c
+++ b/src/basic/lock-util.c
@@ -40,13 +40,13 @@ int make_lock_file_at(int dir_fd, const char *p, int operation, LockFile *ret) {
if (!t)
return -ENOMEM;
- fd = xopenat_lock(dfd,
- p,
- O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY,
- /* xopen_flags = */ 0,
- 0600,
- LOCK_UNPOSIX,
- operation);
+ fd = xopenat_lock_full(dfd,
+ p,
+ O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY,
+ /* xopen_flags = */ 0,
+ 0600,
+ LOCK_UNPOSIX,
+ operation);
if (fd < 0)
return fd == -EAGAIN ? -EBUSY : fd;
diff --git a/src/basic/log.c b/src/basic/log.c
index 1470611..7a44300 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -427,6 +427,8 @@ static int write_to_console(
const char *func,
const char *buffer) {
+ static int dumb = -1;
+
char location[256],
header_time[FORMAT_TIMESTAMP_MAX],
prefix[1 + DECIMAL_STR_MAX(int) + 2],
@@ -438,6 +440,9 @@ static int write_to_console(
if (console_fd < 0)
return 0;
+ if (dumb < 0)
+ dumb = getenv_terminal_is_dumb();
+
if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
xsprintf(prefix, "<%i>", level);
iovec[n++] = IOVEC_MAKE_STRING(prefix);
@@ -481,8 +486,9 @@ static int write_to_console(
/* When writing to a TTY we output an extra '\r' (i.e. CR) first, to generate CRNL rather than just
* NL. This is a robustness thing in case the TTY is currently in raw mode (specifically: has the
* ONLCR flag off). We want that subsequent output definitely starts at the beginning of the line
- * again, after all. If the TTY is not in raw mode the extra CR should not hurt. */
- iovec[n++] = IOVEC_MAKE_STRING(check_console_fd_is_tty() ? "\r\n" : "\n");
+ * again, after all. If the TTY is not in raw mode the extra CR should not hurt. If we're writing to
+ * a dumb terminal, only write NL as CRNL might be interpreted as a double newline. */
+ iovec[n++] = IOVEC_MAKE_STRING(check_console_fd_is_tty() && !dumb ? "\r\n" : "\n");
if (writev(console_fd, iovec, n) < 0) {
diff --git a/src/basic/meson.build b/src/basic/meson.build
index d7450d8..111253e 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -235,7 +235,7 @@ filesystem_includes = ['linux/magic.h',
check_filesystems = find_program('check-filesystems.sh')
r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false)
if r.returncode() != 0
- error('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
+ warning('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
endif
filesystems_gperf_h = custom_target(
diff --git a/src/basic/missing_magic.h b/src/basic/missing_magic.h
index 27a33ad..82d71c8 100644
--- a/src/basic/missing_magic.h
+++ b/src/basic/missing_magic.h
@@ -128,6 +128,11 @@
#define DEVMEM_MAGIC 0x454d444d
#endif
+/* cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */
+#ifndef PID_FS_MAGIC
+#define PID_FS_MAGIC 0x50494446
+#endif
+
/* Not in mainline but included in Ubuntu */
#ifndef SHIFTFS_MAGIC
#define SHIFTFS_MAGIC 0x6a656a62
diff --git a/src/basic/os-util.c b/src/basic/os-util.c
index dbd067f..985d89b 100644
--- a/src/basic/os-util.c
+++ b/src/basic/os-util.c
@@ -61,6 +61,39 @@ bool image_name_is_valid(const char *s) {
return true;
}
+int path_extract_image_name(const char *path, char **ret) {
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ /* Extract last component from path, without any "/" suffixes. */
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+
+ if (r != O_DIRECTORY) {
+ /* Chop off any image suffixes we recognize (unless we already know this must refer to some dir */
+ FOREACH_STRING(suffix, ".sysext.raw", ".confext.raw", ".raw") {
+ char *m = endswith(fn, suffix);
+ if (m) {
+ *m = 0;
+ break;
+ }
+ }
+ }
+
+ /* Truncate the version/counting suffixes */
+ fn[strcspn(fn, "_+")] = 0;
+
+ if (!image_name_is_valid(fn))
+ return -EINVAL;
+
+ *ret = TAKE_PTR(fn);
+ return 0;
+}
+
int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check) {
int r;
@@ -230,9 +263,25 @@ int open_extension_release_at(
continue;
}
- if (!relax_extension_release_check &&
- extension_release_strict_xattr_value(fd, dir_path, de->d_name) != 0)
- continue;
+ if (!relax_extension_release_check) {
+ _cleanup_free_ char *base_image_name = NULL, *base_extension = NULL;
+
+ r = path_extract_image_name(image_name, &base_image_name);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract image name from %s/%s, ignoring: %m", dir_path, de->d_name);
+ continue;
+ }
+
+ r = path_extract_image_name(extension, &base_extension);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", extension);
+ continue;
+ }
+
+ if (!streq(base_image_name, base_extension) &&
+ extension_release_strict_xattr_value(fd, dir_path, image_name) != 0)
+ continue;
+ }
/* We already found what we were looking for, but there's another candidate? We treat this as
* an error, as we want to enforce that there are no ambiguities in case we are in the
diff --git a/src/basic/os-util.h b/src/basic/os-util.h
index 7cee3dd..f6a12a3 100644
--- a/src/basic/os-util.h
+++ b/src/basic/os-util.h
@@ -25,6 +25,7 @@ ImageClass image_class_from_string(const char *s) _pure_;
* in accordance with the OS extension specification, rather than for /usr/lib/ or /etc/os-release. */
bool image_name_is_valid(const char *s) _pure_;
+int path_extract_image_name(const char *path, char **ret);
int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check);
static inline int path_is_os_tree(const char *path) {
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index c54374b..581370d 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -262,11 +262,31 @@ int path_is_network_fs(const char *path) {
return is_network_fs(&s);
}
+int stat_verify_linked(const struct stat *st) {
+ assert(st);
+
+ if (st->st_nlink <= 0)
+ return -EIDRM; /* recognizable error. */
+
+ return 0;
+}
+
+int fd_verify_linked(int fd) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ return stat_verify_linked(&st);
+}
+
int stat_verify_regular(const struct stat *st) {
assert(st);
- /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
- * code. */
+ /* Checks whether the specified stat() structure refers to a regular file. If not returns an
+ * appropriate error code. */
if (S_ISDIR(st->st_mode))
return -EISDIR;
@@ -470,7 +490,7 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(ret);
- fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return fd;
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index ae0aaf8..3501406 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -71,6 +71,9 @@ int path_is_network_fs(const char *path);
*/
#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
+int stat_verify_linked(const struct stat *st);
+int fd_verify_linked(int fd);
+
int stat_verify_regular(const struct stat *st);
int fd_verify_regular(int fd);
int verify_regular_at(int dir_fd, const char *path, bool follow);
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 3355b74..530ef9a 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -1300,7 +1300,7 @@ static bool on_dev_null(void) {
return cached_on_dev_null;
}
-static bool getenv_terminal_is_dumb(void) {
+bool getenv_terminal_is_dumb(void) {
const char *e;
e = getenv("TERM");
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index 2a7d48b..b1d7aee 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -160,6 +160,7 @@ void columns_lines_cache_reset(int _unused_ signum);
void reset_terminal_feature_caches(void);
bool on_tty(void);
+bool getenv_terminal_is_dumb(void);
bool terminal_is_dumb(void);
ColorMode get_color_mode(void);
bool underline_enabled(void);
@@ -186,7 +187,7 @@ static inline bool colors_enabled(void) {
}
static inline const char *ansi_underline(void) {
- return underline_enabled() ? ANSI_UNDERLINE : ANSI_NORMAL;
+ return underline_enabled() ? ANSI_UNDERLINE : "";
}
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 93ccfaa..88357a9 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -178,6 +178,7 @@ static Virtualization detect_vm_dmi_vendor(void) {
{ "VMW", VIRTUALIZATION_VMWARE },
{ "innotek GmbH", VIRTUALIZATION_ORACLE },
{ "VirtualBox", VIRTUALIZATION_ORACLE },
+ { "Oracle Corporation", VIRTUALIZATION_ORACLE }, /* Detect VirtualBox on some proprietary systems via the board_vendor */
{ "Xen", VIRTUALIZATION_XEN },
{ "Bochs", VIRTUALIZATION_BOCHS },
{ "Parallels", VIRTUALIZATION_PARALLELS },
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 5c0f0ab..a3d5607 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -2250,9 +2250,9 @@ static EFI_STATUS initrd_prepare(
assert(ret_initrd_size);
if (entry->type != LOADER_LINUX || !entry->initrd) {
- ret_options = NULL;
- ret_initrd = NULL;
- ret_initrd_size = 0;
+ *ret_options = NULL;
+ *ret_initrd = NULL;
+ *ret_initrd_size = 0;
return EFI_SUCCESS;
}
diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c
index 5b90e17..c4f803c 100644
--- a/src/boot/efi/cpio.c
+++ b/src/boot/efi/cpio.c
@@ -65,7 +65,7 @@ static EFI_STATUS pack_cpio_one(
char *a;
assert(fname);
- assert(contents_size || contents_size == 0);
+ assert(contents || contents_size == 0);
assert(target_dir_prefix);
assert(inode_counter);
assert(cpio_buffer);
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index c95132e..43727ef 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -404,6 +404,11 @@ foreach efi_elf_binary : efi_elf_binaries
if name == 'addon@0@.efi.stub'.format(efi_arch)
efi_addon = exe.full_path()
endif
+
+ test('check-alignment-@0@'.format(name),
+ check_efi_alignment_py,
+ args : exe.full_path(),
+ suite : 'efi')
endforeach
alias_target('systemd-boot', boot_targets)
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 7ef3e76..0d9df7e 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -540,6 +540,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free);
CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free);
+ if (szs[UNIFIED_SECTION_UNAME] > 0)
+ uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
+ szs[UNIFIED_SECTION_UNAME]);
+
/* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
* addons. The data is loaded at once, and then used later. */
err = load_addons(
@@ -614,10 +618,6 @@ static EFI_STATUS run(EFI_HANDLE image) {
/* Show splash screen as early as possible */
graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
- if (szs[UNIFIED_SECTION_UNAME] > 0)
- uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
- szs[UNIFIED_SECTION_UNAME]);
-
if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) {
/* Let's measure the passed kernel command line into the TPM. Note that this possibly
* duplicates what we already did in the boot menu, if that was already used. However, since
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index 39d22f2..01cb896 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -2021,6 +2021,15 @@ static int call(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
+ if (!service_name_is_valid(argv[1]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name: %s", argv[1]);
+ if (!object_path_is_valid(argv[2]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid object path: %s", argv[2]);
+ if (!interface_name_is_valid(argv[3]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid interface name: %s", argv[3]);
+ if (!member_name_is_valid(argv[4]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid member name: %s", argv[4]);
+
r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
if (r < 0)
return bus_log_create_error(r);
diff --git a/src/core/bpf-socket-bind.c b/src/core/bpf-socket-bind.c
index 9f290ab..88ab487 100644
--- a/src/core/bpf-socket-bind.c
+++ b/src/core/bpf-socket-bind.c
@@ -32,6 +32,15 @@ static int update_rules_map(
assert(map_fd >= 0);
+ if (!head) {
+ static const struct socket_bind_rule val = {
+ .address_family = SOCKET_BIND_RULE_AF_MATCH_NOTHING,
+ };
+
+ if (sym_bpf_map_update_elem(map_fd, &i, &val, BPF_ANY) != 0)
+ return -errno;
+ }
+
LIST_FOREACH(socket_bind_items, item, head) {
struct socket_bind_rule val = {
.address_family = (uint32_t) item->address_family,
diff --git a/src/core/bpf/socket_bind/socket-bind-api.bpf.h b/src/core/bpf/socket_bind/socket-bind-api.bpf.h
index 277b9bb..4fe08f1 100644
--- a/src/core/bpf/socket_bind/socket-bind-api.bpf.h
+++ b/src/core/bpf/socket_bind/socket-bind-api.bpf.h
@@ -7,13 +7,17 @@
*/
#include <linux/types.h>
+#include <stdint.h>
/*
* Bind rule is matched with socket fields accessible to cgroup/bind{4,6} hook
* through bpf_sock_addr struct.
- * 'address_family' is expected to be one of AF_UNSPEC, AF_INET or AF_INET6.
+ * 'address_family' is expected to be one of AF_UNSPEC, AF_INET, AF_INET6 or the
+ * magic SOCKET_BIND_RULE_AF_MATCH_NOTHING.
* Matching by family is bypassed for rules with AF_UNSPEC set, which makes the
* rest of a rule applicable for both IPv4 and IPv6 addresses.
+ * If SOCKET_BIND_RULE_AF_MATCH_NOTHING is set the rule fails unconditionally
+ * and other checks are skipped.
* If matching by family is either successful or bypassed, a rule and a socket
* are matched by ip protocol.
* If 'protocol' is 0, matching is bypassed.
@@ -49,3 +53,4 @@ struct socket_bind_rule {
};
#define SOCKET_BIND_MAX_RULES 128
+#define SOCKET_BIND_RULE_AF_MATCH_NOTHING UINT32_MAX
diff --git a/src/core/bpf/socket_bind/socket-bind.bpf.c b/src/core/bpf/socket_bind/socket-bind.bpf.c
index b7972a8..da9f9d1 100644
--- a/src/core/bpf/socket_bind/socket-bind.bpf.c
+++ b/src/core/bpf/socket_bind/socket-bind.bpf.c
@@ -55,6 +55,9 @@ static __always_inline bool match(
__u32 protocol,
__u16 port,
const struct socket_bind_rule *r) {
+ if (r->address_family == SOCKET_BIND_RULE_AF_MATCH_NOTHING)
+ return false;
+
return match_af(address_family, r) &&
match_protocol(protocol, r) &&
match_user_port(port, r);
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c
index 12724c6..2bf9094 100644
--- a/src/core/dynamic-user.c
+++ b/src/core/dynamic-user.c
@@ -337,8 +337,10 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
* the lock on the socket taken. */
k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
- if (k < 0)
+ if (k < 0) {
+ assert(errno_is_valid(-k));
return (int) k;
+ }
*ret_uid = uid;
*ret_lock_fd = lock_fd;
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
index 28d6142..8e6de15 100644
--- a/src/core/exec-invoke.c
+++ b/src/core/exec-invoke.c
@@ -3459,7 +3459,7 @@ static int close_remaining_fds(
const int *fds, size_t n_fds) {
size_t n_dont_close = 0;
- int dont_close[n_fds + 14];
+ int dont_close[n_fds + 15];
assert(params);
@@ -3495,6 +3495,8 @@ static int close_remaining_fds(
if (params->user_lookup_fd >= 0)
dont_close[n_dont_close++] = params->user_lookup_fd;
+ assert(n_dont_close <= ELEMENTSOF(dont_close));
+
return close_all_fds(dont_close, n_dont_close);
}
diff --git a/src/core/main.c b/src/core/main.c
index 3f71cc0..1ed968d 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -627,7 +627,7 @@ static int parse_config_file(void) {
{ "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity },
{ "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type },
{ "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy },
- { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
+ { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_LEGACY, NULL },
{ "Manager", "RuntimeWatchdogSec", config_parse_watchdog_sec, 0, &arg_runtime_watchdog },
{ "Manager", "RuntimeWatchdogPreSec", config_parse_watchdog_sec, 0, &arg_pretimeout_watchdog },
{ "Manager", "RebootWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog },
diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
index e9d567a..1ac2636 100644
--- a/src/core/manager-serialize.c
+++ b/src/core/manager-serialize.c
@@ -153,6 +153,7 @@ int manager_serialize(
}
(void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit);
+ (void) serialize_ratelimit(f, "reload-ratelimit", &m->reload_ratelimit);
bus_track_serialize(m->subscribed, f, "subscribed");
@@ -515,6 +516,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
(void) varlink_server_deserialize_one(m->varlink_server, val, fds);
} else if ((val = startswith(l, "dump-ratelimit=")))
deserialize_ratelimit(&m->dump_ratelimit, "dump-ratelimit", val);
+ else if ((val = startswith(l, "reload-ratelimit=")))
+ deserialize_ratelimit(&m->reload_ratelimit, "reload-ratelimit", val);
else {
ManagerTimestamp q;
diff --git a/src/core/mount.c b/src/core/mount.c
index ded322d..3c4971c 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -55,7 +55,7 @@ static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
-static void mount_enter_dead(Mount *m, MountResult f);
+static void mount_enter_dead(Mount *m, MountResult f, bool flush_result);
static void mount_enter_mounted(Mount *m, MountResult f);
static void mount_cycle_clear(Mount *m);
static int mount_process_proc_self_mountinfo(Manager *m);
@@ -846,7 +846,7 @@ static void mount_catchup(Unit *u) {
break;
case MOUNT_MOUNTED:
assert(!pidref_is_set(&m->control_pid));
- mount_enter_dead(m, MOUNT_SUCCESS);
+ mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
default:
break;
@@ -952,10 +952,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, PidRef *ret_pid) {
return 0;
}
-static void mount_enter_dead(Mount *m, MountResult f) {
+static void mount_enter_dead(Mount *m, MountResult f, bool flush_result) {
assert(m);
- if (m->result == MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS || flush_result)
m->result = f;
unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result));
@@ -983,17 +983,20 @@ static void mount_enter_mounted(Mount *m, MountResult f) {
mount_set_state(m, MOUNT_MOUNTED);
}
-static void mount_enter_dead_or_mounted(Mount *m, MountResult f) {
+static void mount_enter_dead_or_mounted(Mount *m, MountResult f, bool flush_result) {
assert(m);
- /* Enter DEAD or MOUNTED state, depending on what the kernel currently says about the mount point. We use this
- * whenever we executed an operation, so that our internal state reflects what the kernel says again, after all
- * ultimately we just mirror the kernel's internal state on this. */
+ /* Enter DEAD or MOUNTED state, depending on what the kernel currently says about the mount point.
+ * We use this whenever we executed an operation, so that our internal state reflects what
+ * the kernel says again, after all ultimately we just mirror the kernel's internal state on this.
+ *
+ * Note that flush_result only applies to mount_enter_dead(), since that's when the result gets
+ * turned into unit end state. */
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, f);
else
- mount_enter_dead(m, f);
+ mount_enter_dead(m, f, flush_result);
}
static int state_to_kill_operation(MountState state) {
@@ -1049,12 +1052,12 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
else if (state == MOUNT_UNMOUNTING_SIGTERM && m->kill_context.send_sigkill)
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_SUCCESS);
else
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
return;
fail:
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
}
static int mount_set_umount_command(Mount *m, ExecCommand *c) {
@@ -1116,7 +1119,7 @@ static void mount_enter_unmounting(Mount *m) {
return;
fail:
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
}
static int mount_set_mount_command(Mount *m, ExecCommand *c, const MountParameters *p) {
@@ -1232,7 +1235,7 @@ static void mount_enter_mounting(Mount *m) {
return;
fail:
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
}
static void mount_set_reload_result(Mount *m, MountResult result) {
@@ -1298,7 +1301,7 @@ static void mount_enter_remounting(Mount *m) {
fail:
mount_set_reload_result(m, MOUNT_FAILURE_RESOURCES);
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
}
static void mount_cycle_clear(Mount *m) {
@@ -1472,8 +1475,8 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
} else if (streq(key, "control-pid")) {
- pidref_done(&m->control_pid);
- (void) deserialize_pidref(fds, value, &m->control_pid);
+ if (!pidref_is_set(&m->control_pid))
+ (void) deserialize_pidref(fds, value, &m->control_pid);
} else if (streq(key, "control-command")) {
MountExecCommand id;
@@ -1555,7 +1558,8 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (IN_SET(m->state, MOUNT_REMOUNTING, MOUNT_REMOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM))
mount_set_reload_result(m, f);
- else if (m->result == MOUNT_SUCCESS)
+ else if (m->result == MOUNT_SUCCESS && !IN_SET(m->state, MOUNT_MOUNTING, MOUNT_UNMOUNTING))
+ /* MOUNT_MOUNTING and MOUNT_UNMOUNTING states need to be patched, see below. */
m->result = f;
if (m->control_command) {
@@ -1578,15 +1582,15 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
switch (m->state) {
case MOUNT_MOUNTING:
- /* Our mount point has not appeared in mountinfo. Something went wrong. */
+ /* Our mount point has not appeared in mountinfo. Something went wrong. */
if (f == MOUNT_SUCCESS) {
- /* Either /bin/mount has an unexpected definition of success,
- * or someone raced us and we lost. */
+ /* Either /bin/mount has an unexpected definition of success, or someone raced us
+ * and we lost. */
log_unit_warning(UNIT(m), "Mount process finished, but there is no mount.");
f = MOUNT_FAILURE_PROTOCOL;
}
- mount_enter_dead(m, f);
+ mount_enter_dead(m, f, /* flush_result = */ false);
break;
case MOUNT_MOUNTING_DONE:
@@ -1596,13 +1600,11 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case MOUNT_REMOUNTING:
case MOUNT_REMOUNTING_SIGTERM:
case MOUNT_REMOUNTING_SIGKILL:
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
case MOUNT_UNMOUNTING:
-
if (f == MOUNT_SUCCESS && m->from_proc_self_mountinfo) {
-
/* Still a mount point? If so, let's try again. Most likely there were multiple mount points
* stacked on top of each other. We might exceed the timeout specified by the user overall,
* but we will stop as soon as any one umount times out. */
@@ -1613,23 +1615,33 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
mount_enter_unmounting(m);
} else {
log_unit_warning(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
- mount_enter_mounted(m, f);
+ mount_enter_mounted(m, MOUNT_FAILURE_PROTOCOL);
}
+ } else if (f == MOUNT_FAILURE_EXIT_CODE && !m->from_proc_self_mountinfo) {
+ /* Hmm, umount process spawned by us failed, but the mount disappeared anyway?
+ * Maybe someone else is trying to unmount at the same time. */
+ log_unit_notice(u, "Mount disappeared even though umount process failed, continuing.");
+ mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ true);
} else
- mount_enter_dead_or_mounted(m, f);
+ /* At this point, either the unmount succeeded or unexpected error occurred. We usually
+ * remember the first error in 'result', but here let's update that forcibly, since
+ * there could previous failed attempts yet we only care about the most recent
+ * attempt. IOW, if we eventually managed to unmount the stuff, don't enter failed
+ * end state. */
+ mount_enter_dead_or_mounted(m, f, /* flush_result = */ true);
break;
- case MOUNT_UNMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGTERM:
- mount_enter_dead_or_mounted(m, f);
+ case MOUNT_UNMOUNTING_SIGKILL:
+ mount_enter_dead_or_mounted(m, f, /* flush_result = */ false);
break;
case MOUNT_CLEANING:
if (m->clean_result == MOUNT_SUCCESS)
m->clean_result = f;
- mount_enter_dead(m, MOUNT_SUCCESS);
+ mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
default:
@@ -1668,7 +1680,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_SUCCESS);
} else {
log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
}
break;
@@ -1676,7 +1688,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
mount_set_reload_result(m, MOUNT_FAILURE_TIMEOUT);
log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
case MOUNT_UNMOUNTING:
@@ -1690,13 +1702,13 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
log_unit_warning(UNIT(m), "Mount process timed out. Skipping SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT, /* flush_result = */ false);
}
break;
case MOUNT_UNMOUNTING_SIGKILL:
log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT, /* flush_result = */ false);
break;
case MOUNT_CLEANING:
@@ -2157,8 +2169,11 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
switch (mount->state) {
case MOUNT_MOUNTED:
- /* This has just been unmounted by somebody else, follow the state change. */
- mount_enter_dead(mount, MOUNT_SUCCESS);
+ /* This has just been unmounted by somebody else, follow the state change.
+ * Also explicitly override the result (see the comment in mount_sigchld_event()),
+ * but more aggressively here since the state change is extrinsic. */
+ mount_cycle_clear(mount);
+ mount_enter_dead(mount, MOUNT_SUCCESS, /* flush_result = */ true);
break;
case MOUNT_MOUNTING_DONE:
@@ -2166,7 +2181,7 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
* then remove it because of an internal error. E.g., fuse.sshfs seems
* to do that when the connection fails. See #17617. To handle such the
* case, let's once set the state back to mounting. Then, the unit can
- * correctly enter the failed state later in mount_sigchld(). */
+ * correctly enter the failed state later in mount_sigchld_event(). */
mount_set_state(mount, MOUNT_MOUNTING);
break;
@@ -2330,7 +2345,7 @@ static int mount_can_start(Unit *u) {
r = unit_test_start_limit(u);
if (r < 0) {
- mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
+ mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT, /* flush_result = */ false);
return r;
}
diff --git a/src/core/scope.c b/src/core/scope.c
index e4c27da..2841280 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -586,6 +586,8 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F
} else if (streq(key, "pids")) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ /* We don't check if we already received the pid before here because unit_watch_pidref()
+ * does this check internally and discards the new pidref if we already received it before. */
if (deserialize_pidref(fds, value, &pidref) >= 0) {
r = unit_watch_pidref(u, &pidref, /* exclusive= */ false);
if (r < 0)
diff --git a/src/core/service.c b/src/core/service.c
index 060ac08..ffe92d2 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -3174,14 +3174,14 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
s->reload_result = f;
} else if (streq(key, "control-pid")) {
- pidref_done(&s->control_pid);
- (void) deserialize_pidref(fds, value, &s->control_pid);
+ if (!pidref_is_set(&s->control_pid))
+ (void) deserialize_pidref(fds, value, &s->control_pid);
} else if (streq(key, "main-pid")) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
- if (deserialize_pidref(fds, value, &pidref) >= 0)
+ if (!pidref_is_set(&s->main_pid) && deserialize_pidref(fds, value, &pidref) >= 0)
(void) service_set_main_pidref(s, &pidref);
} else if (streq(key, "main-pid-known")) {
@@ -3589,8 +3589,10 @@ static void service_notify_cgroup_empty_event(Unit *u) {
break;
}
- if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0)
- service_enter_start_post(s);
+ if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0) {
+ service_enter_stop_post(s, SERVICE_SUCCESS);
+ break;
+ }
_fallthrough_;
case SERVICE_START_POST:
@@ -3861,11 +3863,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
default:
assert_not_reached();
}
- } else if (s->exit_type == SERVICE_EXIT_CGROUP && s->state == SERVICE_START)
+ } else if (s->exit_type == SERVICE_EXIT_CGROUP && s->state == SERVICE_START &&
+ !IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD, SERVICE_DBUS))
/* If a main process exits very quickly, this function might be executed
* before service_dispatch_exec_io(). Since this function disabled IO events
* to monitor the main process above, we need to update the state here too.
- * Let's consider the process is successfully launched and exited. */
+ * Let's consider the process is successfully launched and exited, but
+ * only when we're not expecting a readiness notification or dbus name. */
service_enter_start_post(s);
}
diff --git a/src/core/show-status.c b/src/core/show-status.c
index 606237e..5b003ba 100644
--- a/src/core/show-status.c
+++ b/src/core/show-status.c
@@ -38,6 +38,8 @@ int parse_show_status(const char *v, ShowStatus *ret) {
int status_vprintf(const char *status, ShowStatusFlags flags, const char *format, va_list ap) {
static const char status_indent[] = " "; /* "[" STATUS "] " */
+ static int dumb = -1;
+
_cleanup_free_ char *s = NULL;
_cleanup_close_ int fd = -EBADF;
struct iovec iovec[7] = {};
@@ -46,6 +48,9 @@ int status_vprintf(const char *status, ShowStatusFlags flags, const char *format
assert(format);
+ if (dumb < 0)
+ dumb = getenv_terminal_is_dumb();
+
/* This is independent of logging, as status messages are
* optional and go exclusively to the console. */
@@ -61,7 +66,7 @@ int status_vprintf(const char *status, ShowStatusFlags flags, const char *format
if (fd < 0)
return fd;
- if (FLAGS_SET(flags, SHOW_STATUS_ELLIPSIZE)) {
+ if (FLAGS_SET(flags, SHOW_STATUS_ELLIPSIZE) && !dumb) {
char *e;
size_t emax, sl;
int c;
@@ -81,7 +86,7 @@ int status_vprintf(const char *status, ShowStatusFlags flags, const char *format
free_and_replace(s, e);
}
- if (prev_ephemeral)
+ if (prev_ephemeral && !dumb)
iovec[n++] = IOVEC_MAKE_STRING(ANSI_REVERSE_LINEFEED "\r" ANSI_ERASE_TO_END_OF_LINE);
if (status) {
@@ -94,9 +99,11 @@ int status_vprintf(const char *status, ShowStatusFlags flags, const char *format
}
iovec[n++] = IOVEC_MAKE_STRING(s);
- iovec[n++] = IOVEC_MAKE_STRING("\r\n"); /* use CRNL instead of just NL, to be robust towards TTYs in raw mode */
+ /* use CRNL instead of just NL, to be robust towards TTYs in raw mode. If we're writing to a dumb
+ * terminal, use NL as CRNL might be interpreted as a double newline. */
+ iovec[n++] = IOVEC_MAKE_STRING(dumb ? "\n" : "\r\n");
- if (prev_ephemeral && !FLAGS_SET(flags, SHOW_STATUS_EPHEMERAL))
+ if (prev_ephemeral && !FLAGS_SET(flags, SHOW_STATUS_EPHEMERAL) && !dumb)
iovec[n++] = IOVEC_MAKE_STRING(ANSI_ERASE_TO_END_OF_LINE);
prev_ephemeral = FLAGS_SET(flags, SHOW_STATUS_EPHEMERAL);
diff --git a/src/core/socket.c b/src/core/socket.c
index 388be62..9adae16 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2634,8 +2634,9 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
else
s->n_refused += k;
} else if (streq(key, "control-pid")) {
- pidref_done(&s->control_pid);
- (void) deserialize_pidref(fds, value, &s->control_pid);
+
+ if (!pidref_is_set(&s->control_pid))
+ (void) deserialize_pidref(fds, value, &s->control_pid);
} else if (streq(key, "control-command")) {
SocketExecCommand id;
diff --git a/src/core/swap.c b/src/core/swap.c
index 488b171..682c2b9 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -989,8 +989,8 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
s->result = f;
} else if (streq(key, "control-pid")) {
- pidref_done(&s->control_pid);
- (void) deserialize_pidref(fds, value, &s->control_pid);
+ if (!pidref_is_set(&s->control_pid))
+ (void) deserialize_pidref(fds, value, &s->control_pid);
} else if (streq(key, "control-command")) {
SwapExecCommand id;
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index 653ad44..2d93e13 100644
--- a/src/cryptenroll/cryptenroll-tpm2.c
+++ b/src/cryptenroll/cryptenroll-tpm2.c
@@ -239,9 +239,9 @@ int enroll_tpm2(struct crypt_device *cd,
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Must provide all PCR values when using TPM2 device key.");
} else {
- r = tpm2_context_new(device, &tpm2_context);
+ r = tpm2_context_new_or_warn(device, &tpm2_context);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) {
r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
index 72be5cc..846679f 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
@@ -85,9 +85,9 @@ int acquire_luks2_key(
}
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
- r = tpm2_context_new(device, &tpm2_context);
+ r = tpm2_context_new_or_warn(device, &tpm2_context);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
r = tpm2_unseal(tpm2_context,
hash_pcr_mask,
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
index d84e5a3..8408bab 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
@@ -14,8 +14,8 @@ int acquire_luks2_key(
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
- const char *pcrlock_path,
const char *pin,
+ const char *pcrlock_path,
uint16_t primary_alg,
const void *key_data,
size_t key_data_size,
diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c
index f59d5f9..e7a38d4 100644
--- a/src/cryptsetup/cryptsetup-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tpm2.c
@@ -139,9 +139,9 @@ int acquire_tpm2_key(
}
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
- r = tpm2_context_new(device, &tpm2_context);
+ r = tpm2_context_new_or_warn(device, &tpm2_context);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
if (!(flags & TPM2_FLAGS_USE_PIN)) {
r = tpm2_unseal(tpm2_context,
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index b56b51a..1822beb 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -846,9 +846,9 @@ static int measure_volume_key(
#if HAVE_TPM2
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
- r = tpm2_context_new(arg_tpm2_device, &c);
+ r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
_cleanup_strv_free_ char **l = NULL;
if (strv_isempty(arg_tpm2_measure_banks)) {
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 92432b6..c858e6a 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -85,7 +85,7 @@ static bool arg_rmdir = false;
static bool arg_in_memory = false;
static char **arg_argv = NULL;
static char *arg_loop_ref = NULL;
-static ImagePolicy* arg_image_policy = NULL;
+static ImagePolicy *arg_image_policy = NULL;
static bool arg_mtree_hash = true;
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -94,6 +94,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
STATIC_DESTRUCTOR_REGISTER(arg_argv, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_loop_ref, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c
index c452531..b8bef53 100644
--- a/src/home/homed-manager.c
+++ b/src/home/homed-manager.c
@@ -1040,7 +1040,7 @@ static int manager_bind_varlink(Manager *m) {
assert(!m->userdb_service);
r = path_extract_filename(socket_path, &m->userdb_service);
if (r < 0)
- return log_error_errno(r, "Failed to extra filename from socket path '%s': %m", socket_path);
+ return log_error_errno(r, "Failed to extract filename from socket path '%s': %m", socket_path);
/* Avoid recursion */
if (setenv("SYSTEMD_BYPASS_USERDB", m->userdb_service, 1) < 0)
diff --git a/src/home/homework-cifs.c b/src/home/homework-cifs.c
index 19f1cd5..5d87131 100644
--- a/src/home/homework-cifs.c
+++ b/src/home/homework-cifs.c
@@ -5,6 +5,7 @@
#include <linux/fs.h>
#endif
+#include "data-fd-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -24,7 +25,7 @@ int home_setup_cifs(
HomeSetupFlags flags,
HomeSetup *setup) {
- _cleanup_free_ char *chost = NULL, *cservice = NULL, *cdir = NULL, *chost_and_service = NULL, *j = NULL;
+ _cleanup_free_ char *chost = NULL, *cservice = NULL, *cdir = NULL, *chost_and_service = NULL, *j = NULL, *options = NULL;
int r;
assert(h);
@@ -53,49 +54,50 @@ int home_setup_cifs(
if (!chost_and_service)
return log_oom();
+ if (asprintf(&options, "user=%s,uid=" UID_FMT ",forceuid,gid=" GID_FMT ",forcegid,file_mode=0%3o,dir_mode=0%3o",
+ user_record_cifs_user_name(h), h->uid, user_record_gid(h), user_record_access_mode(h),
+ user_record_access_mode(h)) < 0)
+ return log_oom();
+
+ if (h->cifs_domain)
+ if (strextendf_with_separator(&options, ",", "domain=%s", h->cifs_domain) < 0)
+ return log_oom();
+
+ if (h->cifs_extra_mount_options)
+ if (!strextend_with_separator(&options, ",", h->cifs_extra_mount_options))
+ return log_oom();
+
r = home_unshare_and_mkdir();
if (r < 0)
return r;
STRV_FOREACH(pw, h->password) {
- _cleanup_(unlink_and_freep) char *p = NULL;
- _cleanup_free_ char *options = NULL;
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_close_ int passwd_fd = -EBADF;
pid_t mount_pid;
int exit_status;
- r = fopen_temporary_child(NULL, &f, &p);
- if (r < 0)
- return log_error_errno(r, "Failed to create temporary credentials file: %m");
-
- fprintf(f,
- "username=%s\n"
- "password=%s\n",
- user_record_cifs_user_name(h),
- *pw);
-
- if (h->cifs_domain)
- fprintf(f, "domain=%s\n", h->cifs_domain);
-
- r = fflush_and_check(f);
- if (r < 0)
- return log_error_errno(r, "Failed to write temporary credentials file: %m");
-
- f = safe_fclose(f);
-
- if (asprintf(&options, "credentials=%s,uid=" UID_FMT ",forceuid,gid=" GID_FMT ",forcegid,file_mode=0%3o,dir_mode=0%3o",
- p, h->uid, user_record_gid(h), user_record_access_mode(h), user_record_access_mode(h)) < 0)
- return log_oom();
-
- if (h->cifs_extra_mount_options)
- if (!strextend_with_separator(&options, ",", h->cifs_extra_mount_options))
- return log_oom();
+ passwd_fd = acquire_data_fd(*pw, strlen(*pw), /* flags= */ 0);
+ if (passwd_fd < 0)
+ return log_error_errno(passwd_fd, "Failed to create data FD for password: %m");
r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid);
if (r < 0)
return r;
if (r == 0) {
/* Child */
+
+ r = fd_cloexec(passwd_fd, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to disable CLOEXEC on password FD: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = setenvf("PASSWD_FD", /* overwrite= */ true, "%d", passwd_fd);
+ if (r < 0) {
+ log_error_errno(errno, "Failed to set $PASSWD_FD: %m");
+ _exit(EXIT_FAILURE);
+ }
+
execl("/bin/mount", "/bin/mount", "-n", "-t", "cifs",
chost_and_service, HOME_RUNTIME_WORK_DIR,
"-o", options, NULL);
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 0919471..aaa52c0 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -47,6 +47,7 @@ static char **arg_file = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_key_pem, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
typedef struct RequestMeta {
sd_journal *journal;
diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c
index da0f20d..294719b 100644
--- a/src/journal-remote/journal-remote-main.c
+++ b/src/journal-remote/journal-remote-main.c
@@ -535,24 +535,6 @@ static int dispatch_http_event(sd_event_source *event,
**********************************************************************
**********************************************************************/
-static int setup_signals(RemoteServer *s) {
- int r;
-
- assert(s);
-
- assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
-
- r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
- if (r < 0)
- return r;
-
- return 0;
-}
-
static int setup_raw_socket(RemoteServer *s, const char *address) {
int fd;
@@ -580,9 +562,9 @@ static int create_remoteserver(
if (r < 0)
return r;
- r = setup_signals(s);
+ r = sd_event_set_signal_exit(s->events, true);
if (r < 0)
- return log_error_errno(r, "Failed to set up signals: %m");
+ return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
n = sd_listen_fds(true);
if (n < 0)
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 79010d0..9db686d 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -376,8 +376,6 @@ void journal_remote_server_destroy(RemoteServer *s) {
writer_unref(s->_single_writer);
hashmap_free(s->writers);
- sd_event_source_unref(s->sigterm_event);
- sd_event_source_unref(s->sigint_event);
sd_event_source_unref(s->listen_event);
sd_event_unref(s->events);
@@ -517,7 +515,9 @@ static int accept_connection(
switch (socket_address_family(addr)) {
case AF_INET:
- case AF_INET6: {
+ case AF_INET6:
+ case AF_VSOCK:
+ case AF_UNIX: {
_cleanup_free_ char *a = NULL;
char *b;
diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h
index 8d73f95..3d64db0 100644
--- a/src/journal-remote/journal-remote.h
+++ b/src/journal-remote/journal-remote.h
@@ -30,7 +30,7 @@ struct RemoteServer {
size_t active;
sd_event *events;
- sd_event_source *sigterm_event, *sigint_event, *listen_event;
+ sd_event_source *listen_event;
Hashmap *writers;
Writer *_single_writer;
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index db74355..d70a049 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -59,6 +59,8 @@ static int arg_follow = -1;
static const char *arg_save_state = NULL;
static usec_t arg_network_timeout_usec = USEC_INFINITY;
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
+
static void close_fd_input(Uploader *u);
#define SERVER_ANSWER_KEEP 2048
@@ -378,38 +380,6 @@ static int open_file_for_upload(Uploader *u, const char *filename) {
return r;
}
-static int dispatch_sigterm(sd_event_source *event,
- const struct signalfd_siginfo *si,
- void *userdata) {
- Uploader *u = ASSERT_PTR(userdata);
-
- log_received_signal(LOG_INFO, si);
-
- close_fd_input(u);
- close_journal_input(u);
-
- sd_event_exit(u->events, 0);
- return 0;
-}
-
-static int setup_signals(Uploader *u) {
- int r;
-
- assert(u);
-
- assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
-
- r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
- if (r < 0)
- return r;
-
- return 0;
-}
-
static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
int r;
const char *host, *proto = "";
@@ -449,9 +419,9 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file)
if (r < 0)
return log_error_errno(r, "sd_event_default failed: %m");
- r = setup_signals(u);
+ r = sd_event_set_signal_exit(u->events, true);
if (r < 0)
- return log_error_errno(r, "Failed to set up signals: %m");
+ return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
(void) sd_watchdog_enabled(false, &u->watchdog_usec);
@@ -475,8 +445,6 @@ static void destroy_uploader(Uploader *u) {
close_fd_input(u);
close_journal_input(u);
- sd_event_source_unref(u->sigterm_event);
- sd_event_source_unref(u->sigint_event);
sd_event_unref(u->events);
}
diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h
index 9ff5a7b..2007864 100644
--- a/src/journal-remote/journal-upload.h
+++ b/src/journal-remote/journal-upload.h
@@ -25,7 +25,6 @@ typedef enum {
typedef struct Uploader {
sd_event *events;
- sd_event_source *sigint_event, *sigterm_event;
char *url;
CURL *easy;
diff --git a/src/journal/cat.c b/src/journal/cat.c
index 609ddba..0325add 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "build.h"
+#include "env-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "main-func.h"
@@ -157,7 +158,6 @@ static int run(int argc, char *argv[]) {
if (argc <= optind)
(void) execl("/bin/cat", "/bin/cat", NULL);
else {
- _cleanup_free_ char *s = NULL;
struct stat st;
if (fstat(STDERR_FILENO, &st) < 0)
@@ -165,11 +165,9 @@ static int run(int argc, char *argv[]) {
"Failed to fstat(%s): %m",
FORMAT_PROC_FD_PATH(STDERR_FILENO));
- if (asprintf(&s, DEV_FMT ":" INO_FMT, (dev_t)st.st_dev, st.st_ino) < 0)
- return log_oom();
-
- if (setenv("JOURNAL_STREAM", s, /* overwrite = */ true) < 0)
- return log_error_errno(errno, "Failed to set environment variable JOURNAL_STREAM: %m");
+ r = setenvf("JOURNAL_STREAM", /* overwrite = */ true, DEV_FMT ":" INO_FMT, (dev_t) st.st_dev, st.st_ino);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set environment variable JOURNAL_STREAM: %m");
(void) execvp(argv[optind], argv + optind);
}
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 7f3dcd5..45ecc96 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -380,7 +380,7 @@ static int help(void) {
" -u --unit=UNIT Show logs from the specified unit\n"
" --user-unit=UNIT Show logs from the specified user unit\n"
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
- " -p --priority=RANGE Show entries with the specified priority\n"
+ " -p --priority=RANGE Show entries within the specified priority range\n"
" --facility=FACILITY... Show entries with the specified facilities\n"
" -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
" --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n"
@@ -1938,6 +1938,7 @@ static int update_cursor(sd_journal *j) {
typedef struct Context {
sd_journal *journal;
+ bool has_cursor;
bool need_seek;
bool since_seeked;
bool ellipsized;
@@ -1967,11 +1968,11 @@ static int show(Context *c) {
break;
}
- if (arg_until_set && !arg_reverse && (arg_lines < 0 || arg_since_set)) {
- /* If --lines= is set, we usually rely on the n_shown to tell us
- * when to stop. However, if --since= is set too, we may end up
- * having less than --lines= to output. In this case let's also
- * check if the entry is in range. */
+ if (arg_until_set && !arg_reverse && (arg_lines < 0 || arg_since_set || c->has_cursor)) {
+ /* If --lines= is set, we usually rely on the n_shown to tell us when to stop.
+ * However, if --since= or one of the cursor argument is set too, we may end up
+ * having less than --lines= to output. In this case let's also check if the entry
+ * is in range. */
usec_t usec;
@@ -2572,6 +2573,7 @@ static int run(int argc, char *argv[]) {
Context c = {
.journal = j,
+ .has_cursor = cursor,
.need_seek = need_seek,
.since_seeked = since_seeked,
};
diff --git a/src/kernel-install/60-ukify.install.in b/src/kernel-install/60-ukify.install.in
index be1e21b..0f7a0db 100755
--- a/src/kernel-install/60-ukify.install.in
+++ b/src/kernel-install/60-ukify.install.in
@@ -109,6 +109,12 @@ def parse_args(args=None):
return opts
def we_are_wanted() -> bool:
+ KERNEL_INSTALL_IMAGE_TYPE = os.getenv('KERNEL_INSTALL_IMAGE_TYPE')
+
+ if KERNEL_INSTALL_IMAGE_TYPE == 'uki':
+ log('The image being installed is already a UKI, quitting.')
+ return False
+
KERNEL_INSTALL_LAYOUT = os.getenv('KERNEL_INSTALL_LAYOUT')
if KERNEL_INSTALL_LAYOUT != 'uki':
diff --git a/src/kernel-install/90-uki-copy.install b/src/kernel-install/90-uki-copy.install
index c66c097..d443c4b 100755
--- a/src/kernel-install/90-uki-copy.install
+++ b/src/kernel-install/90-uki-copy.install
@@ -26,8 +26,6 @@ KERNEL_VERSION="${2:?}"
ENTRY_DIR_ABS="$3"
KERNEL_IMAGE="$4"
-[ "$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
-
ENTRY_TOKEN="$KERNEL_INSTALL_ENTRY_TOKEN"
BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT"
@@ -48,6 +46,8 @@ case "$COMMAND" in
;;
esac
+[ "$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
+
if ! [ -d "$UKI_DIR" ]; then
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "creating $UKI_DIR"
mkdir -p "$UKI_DIR"
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index 5e216c5..5679091 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -10,6 +10,8 @@
#include "alloc-util.h"
#include "dhcp-option.h"
#include "dhcp-server-internal.h"
+#include "dns-domain.h"
+#include "hostname-util.h"
#include "memory-util.h"
#include "ordered-set.h"
#include "strv.h"
@@ -396,27 +398,56 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t c
}
int dhcp_option_parse_string(const uint8_t *option, size_t len, char **ret) {
+ _cleanup_free_ char *string = NULL;
int r;
assert(option);
assert(ret);
- if (len <= 0)
- *ret = mfree(*ret);
- else {
- char *string;
+ if (len <= 0) {
+ *ret = NULL;
+ return 0;
+ }
- /*
- * One trailing NUL byte is OK, we don't mind. See:
- * https://github.com/systemd/systemd/issues/1337
- */
- r = make_cstring((const char *) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
- if (r < 0)
- return r;
+ /* One trailing NUL byte is OK, we don't mind. See:
+ * https://github.com/systemd/systemd/issues/1337 */
+ r = make_cstring((const char *) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
+ if (r < 0)
+ return r;
+
+ if (!string_is_safe(string) || !utf8_is_valid(string))
+ return -EINVAL;
+
+ *ret = TAKE_PTR(string);
+ return 0;
+}
+
+int dhcp_option_parse_hostname(const uint8_t *option, size_t len, char **ret) {
+ _cleanup_free_ char *hostname = NULL;
+ int r;
- free_and_replace(*ret, string);
+ assert(option);
+ assert(ret);
+
+ r = dhcp_option_parse_string(option, len, &hostname);
+ if (r < 0)
+ return r;
+
+ if (!hostname) {
+ *ret = NULL;
+ return 0;
}
+ if (!hostname_is_valid(hostname, 0))
+ return -EINVAL;
+
+ r = dns_name_is_valid(hostname);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ *ret = TAKE_PTR(hostname);
return 0;
}
diff --git a/src/libsystemd-network/dhcp-option.h b/src/libsystemd-network/dhcp-option.h
index 425f5b5..aaa8f84 100644
--- a/src/libsystemd-network/dhcp-option.h
+++ b/src/libsystemd-network/dhcp-option.h
@@ -44,3 +44,4 @@ int dhcp_option_parse(
char **ret_error_message);
int dhcp_option_parse_string(const uint8_t *option, size_t len, char **ret);
+int dhcp_option_parse_hostname(const uint8_t *option, size_t len, char **ret);
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 4e3be98..202d75f 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -833,12 +833,16 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
- case SD_DHCP_OPTION_ROOT_PATH:
- r = dhcp_option_parse_string(option, len, &lease->root_path);
+ case SD_DHCP_OPTION_ROOT_PATH: {
+ _cleanup_free_ char *p = NULL;
+
+ r = dhcp_option_parse_string(option, len, &p);
if (r < 0)
log_debug_errno(r, "Failed to parse root path, ignoring: %m");
- break;
+ free_and_replace(lease->root_path, p);
+ break;
+ }
case SD_DHCP_OPTION_RENEWAL_TIME:
r = lease_parse_be32_seconds(option, len, /* max_as_infinity = */ true, &lease->t1);
if (r < 0)
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index fcc5b74..b87e4d6 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -808,14 +808,16 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
req->agent_info_option = (uint8_t*)option - 2;
break;
- case SD_DHCP_OPTION_HOST_NAME:
- r = dhcp_option_parse_string(option, len, &req->hostname);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
- return 0;
- }
+ case SD_DHCP_OPTION_HOST_NAME: {
+ _cleanup_free_ char *p = NULL;
+ r = dhcp_option_parse_hostname(option, len, &p);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
+ else
+ free_and_replace(req->hostname, p);
break;
+ }
case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
req->parameter_request_list = option;
req->parameter_request_list_len = len;
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index 77b2e1a..f415797 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -277,14 +277,16 @@ _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *fo
va_start(ap, format);
r = sd_bus_error_setfv(e, name, format, ap);
- assert(!name || r < 0);
+ if (name)
+ assert(r < 0);
va_end(ap);
return r;
}
r = sd_bus_error_set(e, name, NULL);
- assert(!name || r < 0);
+ if (name)
+ assert(r < 0);
return r;
}
diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h
index b903d1a..e8a6d52 100644
--- a/src/libsystemd/sd-device/device-private.h
+++ b/src/libsystemd/sd-device/device-private.h
@@ -20,7 +20,10 @@ int device_opendir(sd_device *device, const char *subdir, DIR **ret);
int device_get_property_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
-int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
+int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);
+static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+ return device_get_sysattr_unsigned_full(device, sysattr, 0, ret_value);
+}
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 2fbc619..01e66b4 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -2435,7 +2435,7 @@ int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_valu
return v > 0;
}
-int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value) {
const char *value;
int r;
@@ -2444,7 +2444,7 @@ int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned
return r;
unsigned v;
- r = safe_atou(value, &v);
+ r = safe_atou_full(value, base, &v);
if (r < 0)
return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 288798a..b6899df 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -2415,7 +2415,7 @@ static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
if (wd < 0)
- return -errno;
+ return wd;
if (d->wd < 0) {
r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d);
@@ -2637,7 +2637,7 @@ _public_ int sd_event_source_get_io_fd(sd_event_source *s) {
}
_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
- int r;
+ int saved_fd, r;
assert_return(s, -EINVAL);
assert_return(fd >= 0, -EBADF);
@@ -2647,16 +2647,12 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
if (s->io.fd == fd)
return 0;
- if (event_source_is_offline(s)) {
- s->io.fd = fd;
- s->io.registered = false;
- } else {
- int saved_fd;
+ saved_fd = s->io.fd;
+ s->io.fd = fd;
- saved_fd = s->io.fd;
- assert(s->io.registered);
+ assert(event_source_is_offline(s) == !s->io.registered);
- s->io.fd = fd;
+ if (s->io.registered) {
s->io.registered = false;
r = source_io_register(s, s->enabled, s->io.events);
@@ -2669,6 +2665,9 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
(void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
}
+ if (s->io.owned)
+ safe_close(saved_fd);
+
return 0;
}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 63d3ee7..cc3d84e 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -828,6 +828,24 @@ TEST(fork) {
assert_se(r >= 0);
}
+TEST(sd_event_source_set_io_fd) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_close_pair_ int pfd_a[2] = EBADF_PAIR, pfd_b[2] = EBADF_PAIR;
+
+ assert_se(sd_event_default(&e) >= 0);
+
+ assert_se(pipe2(pfd_a, O_CLOEXEC) >= 0);
+ assert_se(pipe2(pfd_b, O_CLOEXEC) >= 0);
+
+ assert_se(sd_event_add_io(e, &s, pfd_a[0], EPOLLIN, NULL, INT_TO_PTR(-ENOANO)) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+ TAKE_FD(pfd_a[0]);
+
+ assert_se(sd_event_source_set_io_fd(s, pfd_b[0]) >= 0);
+ TAKE_FD(pfd_b[0]);
+}
+
static int hup_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
unsigned *c = userdata;
diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c
index 94bfd70..b9714ee 100644
--- a/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libsystemd/sd-id128/id128-util.c
@@ -138,7 +138,7 @@ int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
- fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return fd;
@@ -184,7 +184,7 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
- fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
+ fd = xopenat_full(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
if (fd < 0)
return fd;
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
index d2493a0..08cbf86 100644
--- a/src/libsystemd/sd-journal/journal-file.c
+++ b/src/libsystemd/sd-journal/journal-file.c
@@ -639,7 +639,7 @@ static int journal_file_verify_header(JournalFile *f) {
return -ENODATA;
if (!VALID_REALTIME(le64toh(f->header->tail_entry_realtime)))
return -ENODATA;
- if (!VALID_MONOTONIC(le64toh(f->header->tail_entry_realtime)))
+ if (!VALID_MONOTONIC(le64toh(f->header->tail_entry_monotonic)))
return -ENODATA;
} else {
/* Otherwise, the fields must be zero. */
@@ -650,7 +650,7 @@ static int journal_file_verify_header(JournalFile *f) {
return -ENODATA;
if (f->header->tail_entry_realtime != 0)
return -ENODATA;
- if (f->header->tail_entry_realtime != 0)
+ if (f->header->tail_entry_monotonic != 0)
return -ENODATA;
}
}
@@ -736,8 +736,9 @@ int journal_file_fstat(JournalFile *f) {
return r;
/* Refuse appending to files that are already deleted */
- if (f->last_stat.st_nlink <= 0)
- return -EIDRM;
+ r = stat_verify_linked(&f->last_stat);
+ if (r < 0)
+ return r;
return 0;
}
@@ -2532,7 +2533,7 @@ int journal_file_append_entry(
ts->realtime);
if (!VALID_MONOTONIC(ts->monotonic))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
- "Invalid monotomic timestamp %" PRIu64 ", refusing entry.",
+ "Invalid monotonic timestamp %" PRIu64 ", refusing entry.",
ts->monotonic);
} else {
dual_timestamp_now(&_ts);
diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c
index bdaa01d..b5ce55a 100644
--- a/src/libsystemd/sd-journal/journal-verify.c
+++ b/src/libsystemd/sd-journal/journal-verify.c
@@ -162,7 +162,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
int r;
if (le64toh(o->data.entry_offset) == 0)
- warning(offset, "Unused data (entry_offset==0)");
+ debug(offset, "Unused data (entry_offset==0)");
if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
error(offset, "Bad n_entries: %"PRIu64, le64toh(o->data.n_entries));
diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c
index 6b9ff0a..ca1ef0c 100644
--- a/src/libsystemd/sd-journal/sd-journal.c
+++ b/src/libsystemd/sd-journal/sd-journal.c
@@ -1720,7 +1720,7 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask)
m->wd = inotify_add_watch_fd(j->inotify_fd, fd, mask);
if (m->wd < 0) {
- log_debug_errno(errno, "Failed to watch journal directory '%s', ignoring: %m", m->path);
+ log_debug_errno(m->wd, "Failed to watch journal directory '%s', ignoring: %m", m->path);
return;
}
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index ec1f2f3..cd2db2d 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -2072,7 +2072,7 @@ static int method_do_shutdown_or_sleep(
case SLEEP_RESUME_NOT_SUPPORTED:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
- "Not running on EFI and resume= is not set. No available method to resume from hibernation");
+ "Not running on EFI and resume= is not set, or noresume is set. No available method to resume from hibernation");
case SLEEP_NOT_ENOUGH_SWAP_SPACE:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c
index 3a95ba8..bba84bb 100644
--- a/src/network/networkd-state-file.c
+++ b/src/network/networkd-state-file.c
@@ -127,7 +127,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
NDiscRDNSS *a;
SET_FOREACH(a, link->ndisc_rdnss) {
- r = ordered_set_put_in6_addrv(s, &a->router, 1);
+ r = ordered_set_put_in6_addrv(s, &a->address, 1);
if (r < 0)
return r;
}
@@ -190,7 +190,7 @@ static int link_put_sip(Link *link, OrderedSet **s) {
assert(link->network);
assert(s);
- if (link->dhcp_lease && link->network->dhcp_use_ntp) {
+ if (link->dhcp_lease && link->network->dhcp_use_sip) {
const struct in_addr *addresses;
r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c
index f20f410..f9b9437 100644
--- a/src/network/tc/qdisc.c
+++ b/src/network/tc/qdisc.c
@@ -293,14 +293,20 @@ QDisc* qdisc_drop(QDisc *qdisc) {
link = ASSERT_PTR(qdisc->link);
+ qdisc_mark(qdisc); /* To avoid stack overflow. */
+
/* also drop all child classes assigned to the qdisc. */
SET_FOREACH(tclass, link->tclasses) {
+ if (tclass_is_marked(tclass))
+ continue;
+
if (TC_H_MAJ(tclass->classid) != qdisc->handle)
continue;
tclass_drop(tclass);
}
+ qdisc_unmark(qdisc);
qdisc_enter_removed(qdisc);
if (qdisc->state == 0) {
diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c
index 0a5fec0..394e06d 100644
--- a/src/network/tc/tclass.c
+++ b/src/network/tc/tclass.c
@@ -260,14 +260,20 @@ TClass* tclass_drop(TClass *tclass) {
link = ASSERT_PTR(tclass->link);
+ tclass_mark(tclass); /* To avoid stack overflow. */
+
/* Also drop all child qdiscs assigned to the class. */
SET_FOREACH(qdisc, link->qdiscs) {
+ if (qdisc_is_marked(qdisc))
+ continue;
+
if (qdisc->parent != tclass->classid)
continue;
qdisc_drop(qdisc);
}
+ tclass_unmark(tclass);
tclass_enter_removed(tclass);
if (tclass->state == 0) {
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 5487aaf..4fabe1b 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -3839,9 +3839,9 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Must provide all PCR values when using TPM2 device key.");
} else {
- r = tpm2_context_new(arg_tpm2_device, &tpm2_context);
+ r = tpm2_context_new_or_warn(arg_tpm2_device, &tpm2_context);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) {
r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c
index 1295949..394c258 100644
--- a/src/pcrextend/pcrextend.c
+++ b/src/pcrextend/pcrextend.c
@@ -199,7 +199,7 @@ static int extend_now(unsigned pcr, const void *data, size_t size, Tpm2Userspace
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
int r;
- r = tpm2_context_new(arg_tpm2_device, &c);
+ r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
if (r < 0)
return r;
diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c
index bdc6bbd..dde4dd9 100644
--- a/src/pcrlock/pcrlock.c
+++ b/src/pcrlock/pcrlock.c
@@ -1194,7 +1194,7 @@ static int event_log_read_pcrs(EventLog *el) {
assert(el);
- r = tpm2_context_new(NULL, &tc);
+ r = tpm2_context_new_or_warn(/* device= */ NULL, &tc);
if (r < 0)
return r;
@@ -4281,9 +4281,9 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
}
_cleanup_(tpm2_context_unrefp) Tpm2Context *tc = NULL;
- r = tpm2_context_new(NULL, &tc);
+ r = tpm2_context_new_or_warn(/* device= */ NULL, &tc);
if (r < 0)
- return log_error_errno(r, "Failed to allocate TPM2 context: %m");
+ return r;
if (!tpm2_supports_command(tc, TPM2_CC_PolicyAuthorizeNV))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 does not support PolicyAuthorizeNV command, refusing.");
@@ -4610,7 +4610,7 @@ static int undefine_policy_nv_index(
assert(srk_blob);
_cleanup_(tpm2_context_unrefp) Tpm2Context *tc = NULL;
- r = tpm2_context_new(NULL, &tc);
+ r = tpm2_context_new_or_warn(/* device= */ NULL, &tc);
if (r < 0)
return r;
diff --git a/src/portable/portable.c b/src/portable/portable.c
index 6054f0f..3b2a379 100644
--- a/src/portable/portable.c
+++ b/src/portable/portable.c
@@ -1623,9 +1623,8 @@ int portable_attach(
return 0;
}
-static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths) {
+static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths, bool match_all) {
_cleanup_strv_free_ char **root_and_extensions = NULL;
- const char *a;
int r;
assert(marker);
@@ -1635,7 +1634,9 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
* list of images/paths. We enforce strict 1:1 matching, so that we are sure
* we are detaching exactly what was attached.
* For each image, starting with the root, we look for a token in the marker,
- * and return a negative answer on any non-matching combination. */
+ * and return a negative answer on any non-matching combination.
+ * If a partial match is allowed, then return immediately once it is found, otherwise
+ * ensure that everything matches. */
root_and_extensions = strv_new(name_or_path);
if (!root_and_extensions)
@@ -1645,70 +1646,33 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
if (r < 0)
return r;
- STRV_FOREACH(image_name_or_path, root_and_extensions) {
- _cleanup_free_ char *image = NULL;
+ /* Ensure the number of images passed matches the number of images listed in the marker */
+ while (!isempty(marker))
+ STRV_FOREACH(image_name_or_path, root_and_extensions) {
+ _cleanup_free_ char *image = NULL, *base_image = NULL, *base_image_name_or_path = NULL;
- r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
- if (r < 0)
- return log_debug_errno(r, "Failed to parse marker: %s", marker);
- if (r == 0)
- return false;
-
- a = last_path_component(image);
-
- if (image_name_is_valid(*image_name_or_path)) {
- const char *e, *underscore;
-
- /* We shall match against an image name. In that case let's compare the last component, and optionally
- * allow either a suffix of ".raw" or a series of "/".
- * But allow matching on a different version of the same image, when a "_" is used as a separator. */
- underscore = strchr(*image_name_or_path, '_');
- if (underscore) {
- if (strneq(a, *image_name_or_path, underscore - *image_name_or_path))
- continue;
- return false;
- }
-
- e = startswith(a, *image_name_or_path);
- if (!e)
- return false;
-
- if(!(e[strspn(e, "/")] == 0 || streq(e, ".raw")))
- return false;
- } else {
- const char *b, *underscore;
- size_t l;
-
- /* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
- * reach the same file. However, in this mode, let's validate any file suffix.
- * But also ensure that we don't fail if both components don't have a '/' at all
- * (strcspn returns the full length of the string in that case, which might not
- * match as the versions might differ). */
-
- l = strcspn(a, "/");
- b = last_path_component(*image_name_or_path);
-
- if ((a[l] != '/') != !strchr(b, '/')) /* One is a directory, the other is not */
+ r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse marker: %s", marker);
+ if (r == 0)
return false;
- if (a[l] != 0 && strcspn(b, "/") != l)
- return false;
+ r = path_extract_image_name(image, &base_image);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image);
- underscore = strchr(b, '_');
- if (underscore)
- l = underscore - b;
- else { /* Either component could be versioned */
- underscore = strchr(a, '_');
- if (underscore)
- l = underscore - a;
- }
+ r = path_extract_image_name(*image_name_or_path, &base_image_name_or_path);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", *image_name_or_path);
- if (!strneq(a, b, l))
- return false;
+ if (!streq(base_image, base_image_name_or_path)) {
+ if (match_all)
+ return false;
+ } else if (!match_all)
+ return true;
}
- }
- return true;
+ return match_all;
}
static int test_chroot_dropin(
@@ -1763,7 +1727,9 @@ static int test_chroot_dropin(
if (!name_or_path)
r = true;
else
- r = marker_matches_images(marker, name_or_path, extension_image_paths);
+ /* When detaching we want to match exactly on all images, but when inspecting we only need
+ * to get the state of one component */
+ r = marker_matches_images(marker, name_or_path, extension_image_paths, ret_marker != NULL);
if (ret_marker)
*ret_marker = TAKE_PTR(marker);
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 1ef25ac..75ba29c 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -13,6 +13,7 @@
#include "missing_capability.h"
#include "resolved-bus.h"
#include "resolved-def.h"
+#include "resolved-dns-stream.h"
#include "resolved-dns-synthesize.h"
#include "resolved-dnssd-bus.h"
#include "resolved-dnssd.h"
@@ -1832,6 +1833,7 @@ static int bus_method_reset_server_features(sd_bus_message *message, void *userd
bus_client_log(message, "server feature reset");
+ (void) dns_stream_disconnect_all(m);
manager_reset_server_features(m);
return sd_bus_reply_method_return(message, NULL);
@@ -2218,9 +2220,15 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b
if (b)
return 0;
- log_debug("Coming back from suspend, verifying all RRs...");
+ log_debug("Coming back from suspend, closing all TCP connections...");
+ (void) dns_stream_disconnect_all(m);
+
+ log_debug("Coming back from suspend, resetting all probed server features...");
+ manager_reset_server_features(m);
+ log_debug("Coming back from suspend, verifying all RRs...");
manager_verify_all(m);
+
return 0;
}
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index a9a6492..e90915e 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -531,6 +531,20 @@ static int dns_cache_put_positive(
TAKE_PTR(i);
return 0;
}
+/* https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml */
+/* https://www.iana.org/assignments/locally-served-dns-zones/locally-served-dns-zones.xhtml#transport-independent */
+static bool dns_special_use_domain_invalid_answer(DnsResourceKey *key, int rcode) {
+ /* Sometimes we know a domain exists, even if broken nameservers say otherwise. Make sure not to
+ * cache any answers we know are wrong. */
+
+ /* RFC9462 § 6.4: resolvers SHOULD respond to queries of any type other than SVCB for
+ * _dns.resolver.arpa. with NODATA and queries of any type for any domain name under resolver.arpa
+ * with NODATA. */
+ if (dns_name_endswith(dns_resource_key_name(key), "resolver.arpa") > 0 && rcode == DNS_RCODE_NXDOMAIN)
+ return true;
+
+ return false;
+}
static int dns_cache_put_negative(
DnsCache *c,
@@ -561,6 +575,8 @@ static int dns_cache_put_negative(
return 0;
if (dns_type_is_pseudo(key->type))
return 0;
+ if (dns_special_use_domain_invalid_answer(key, rcode))
+ return 0;
if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
if (!soa)
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 7eb6b97..16334c6 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -57,6 +57,21 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
}
}
+static void dns_query_candidate_abandon(DnsQueryCandidate *c) {
+ DnsTransaction *t;
+
+ assert(c);
+
+ /* Abandon all the DnsTransactions attached to this query */
+
+ while ((t = set_steal_first(c->transactions))) {
+ t->wait_for_answer = true;
+ set_remove(t->notify_query_candidates, c);
+ set_remove(t->notify_query_candidates_done, c);
+ dns_transaction_gc(t);
+ }
+}
+
static DnsQueryCandidate* dns_query_candidate_unlink(DnsQueryCandidate *c) {
assert(c);
@@ -354,6 +369,16 @@ static void dns_query_stop(DnsQuery *q) {
dns_query_candidate_stop(c);
}
+static void dns_query_abandon(DnsQuery *q) {
+ assert(q);
+
+ /* Thankfully transactions have their own timeouts */
+ event_source_disable(q->timeout_event_source);
+
+ LIST_FOREACH(candidates_by_query, c, q->candidates)
+ dns_query_candidate_abandon(c);
+}
+
static void dns_query_unlink_candidates(DnsQuery *q) {
assert(q);
@@ -588,7 +613,7 @@ void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
(void) manager_monitor_send(q->manager, q->state, q->answer_rcode, q->answer_errno, q->question_idna, q->question_utf8, q->question_bypass, q->collected_questions, q->answer);
- dns_query_stop(q);
+ dns_query_abandon(q);
if (q->complete)
q->complete(q);
}
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index 00f7bea..b280a5a 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -181,6 +181,23 @@ bool dns_resource_key_is_dnssd_ptr(const DnsResourceKey *key) {
dns_name_endswith(dns_resource_key_name(key), "_udp.local");
}
+bool dns_resource_key_is_dnssd_two_label_ptr(const DnsResourceKey *key) {
+ assert(key);
+
+ /* Check if this is a PTR resource key used in Service Instance
+ * Enumeration as described in RFC6763 § 4.1, excluding selective
+ * service names described in RFC6763 § 7.1. */
+
+ if (key->type != DNS_TYPE_PTR)
+ return false;
+
+ const char *name = dns_resource_key_name(key);
+ if (dns_name_parent(&name) <= 0)
+ return false;
+
+ return dns_name_equal(name, "_tcp.local") || dns_name_equal(name, "_udp.local");
+}
+
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
int r;
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index fd15cc3..1a12933 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -305,6 +305,7 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
const char* dns_resource_key_name(const DnsResourceKey *key);
bool dns_resource_key_is_address(const DnsResourceKey *key);
bool dns_resource_key_is_dnssd_ptr(const DnsResourceKey *key);
+bool dns_resource_key_is_dnssd_two_label_ptr(const DnsResourceKey *key);
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain);
int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain);
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 2e8b3e5..af8e9cd 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -424,7 +424,15 @@ static int dns_scope_socket(
return r;
}
- if (ifindex != 0) {
+ bool addr_is_nonlocal = s->link &&
+ !manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) &&
+ in_addr_is_localhost(sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) == 0;
+
+ if (addr_is_nonlocal && ifindex != 0) {
+ /* As a special exception we don't use UNICAST_IF if we notice that the specified IP address
+ * is on the local host. Otherwise, destination addresses on the local host result in
+ * EHOSTUNREACH, since Linux won't send the packets out of the specified interface, but
+ * delivers them directly to the local socket. */
r = socket_set_unicast_if(fd, sa.sa.sa_family, ifindex);
if (r < 0)
return r;
@@ -463,19 +471,13 @@ static int dns_scope_socket(
else {
bool bound = false;
- /* Let's temporarily bind the socket to the specified ifindex. The kernel currently takes
- * only the SO_BINDTODEVICE/SO_BINDTOINDEX ifindex into account when making routing decisions
+ /* Let's temporarily bind the socket to the specified ifindex. Older kernels only take
+ * the SO_BINDTODEVICE/SO_BINDTOINDEX ifindex into account when making routing decisions
* in connect() — and not IP_UNICAST_IF. We don't really want any of the other semantics of
* SO_BINDTODEVICE/SO_BINDTOINDEX, hence we immediately unbind the socket after the fact
* again.
- *
- * As a special exception we don't do this if we notice that the specified IP address is on
- * the local host. SO_BINDTODEVICE in combination with destination addresses on the local
- * host result in EHOSTUNREACH, since Linux won't send the packets out of the specified
- * interface, but delivers them directly to the local socket. */
- if (s->link &&
- !manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) &&
- in_addr_is_localhost(sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) == 0) {
+ */
+ if (addr_is_nonlocal) {
r = socket_bind_to_ifindex(fd, ifindex);
if (r < 0)
return r;
@@ -589,6 +591,29 @@ static DnsScopeMatch match_subnet_reverse_lookups(
return _DNS_SCOPE_MATCH_INVALID;
}
+/* https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml */
+/* https://www.iana.org/assignments/locally-served-dns-zones/locally-served-dns-zones.xhtml */
+static bool dns_refuse_special_use_domain(const char *domain, DnsQuestion *question) {
+ /* RFC9462 § 6.4: resolvers SHOULD respond to queries of any type other than SVCB for
+ * _dns.resolver.arpa. with NODATA and queries of any type for any domain name under
+ * resolver.arpa with NODATA. */
+ if (dns_name_equal(domain, "_dns.resolver.arpa") > 0) {
+ DnsResourceKey *t;
+
+ /* Only SVCB is permitted to _dns.resolver.arpa */
+ DNS_QUESTION_FOREACH(t, question)
+ if (t->type == DNS_TYPE_SVCB)
+ return false;
+
+ return true;
+ }
+
+ if (dns_name_endswith(domain, "resolver.arpa") > 0)
+ return true;
+
+ return false;
+}
+
DnsScopeMatch dns_scope_good_domain(
DnsScope *s,
DnsQuery *q) {
@@ -601,6 +626,7 @@ DnsScopeMatch dns_scope_good_domain(
/* This returns the following return values:
*
* DNS_SCOPE_NO → This scope is not suitable for lookups of this domain, at all
+ * DNS_SCOPE_LAST_RESORT→ This scope is not suitable, unless we have no alternative
* DNS_SCOPE_MAYBE → This scope is suitable, but only if nothing else wants it
* DNS_SCOPE_YES_BASE+n → This scope is suitable, and 'n' suffix labels match
*
@@ -643,6 +669,10 @@ DnsScopeMatch dns_scope_good_domain(
if (dns_name_dont_resolve(domain))
return DNS_SCOPE_NO;
+ /* Avoid asking invalid questions of some special use domains */
+ if (dns_refuse_special_use_domain(domain, question))
+ return DNS_SCOPE_NO;
+
/* Never go to network for the _gateway, _outbound, _localdnsstub, _localdnsproxy domain — they're something special, synthesized locally. */
if (is_gateway_hostname(domain) ||
is_outbound_hostname(domain) ||
@@ -749,7 +779,7 @@ DnsScopeMatch dns_scope_good_domain(
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
- return DNS_SCOPE_MAYBE;
+ return DNS_SCOPE_LAST_RESORT;
if ((dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */
@@ -772,7 +802,7 @@ DnsScopeMatch dns_scope_good_domain(
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
- return DNS_SCOPE_MAYBE;
+ return DNS_SCOPE_LAST_RESORT;
if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
dns_name_equal(domain, "local") == 0 && /* don't resolve "local" with LLMNR, it's the top-level domain of mDNS after all, see above */
@@ -1459,9 +1489,10 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
continue;
}
- /* Collect service types for _services._dns-sd._udp.local RRs in a set */
+ /* Collect service types for _services._dns-sd._udp.local RRs in a set. Only two-label names
+ * (not selective names) are considered according to RFC6763 § 9. */
if (!scope->announced &&
- dns_resource_key_is_dnssd_ptr(z->rr->key)) {
+ dns_resource_key_is_dnssd_two_label_ptr(z->rr->key)) {
if (!set_contains(types, dns_resource_key_name(z->rr->key))) {
r = set_ensure_put(&types, &dns_name_hash_ops, dns_resource_key_name(z->rr->key));
if (r < 0)
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index ca33fd0..b1d1206 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -18,6 +18,7 @@ typedef struct DnsScope DnsScope;
typedef enum DnsScopeMatch {
DNS_SCOPE_NO,
+ DNS_SCOPE_LAST_RESORT,
DNS_SCOPE_MAYBE,
DNS_SCOPE_YES_BASE, /* Add the number of matching labels to this */
DNS_SCOPE_YES_END = DNS_SCOPE_YES_BASE + DNS_N_LABELS_MAX,
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
index ddd1db5..056ba77 100644
--- a/src/resolve/resolved-dns-stream.c
+++ b/src/resolve/resolved-dns-stream.c
@@ -593,3 +593,44 @@ void dns_stream_detach(DnsStream *s) {
dns_server_unref_stream(s->server);
}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ dns_stream_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ dns_stream_unref);
+
+int dns_stream_disconnect_all(Manager *m) {
+ _cleanup_(set_freep) Set *closed = NULL;
+ int r;
+
+ assert(m);
+
+ /* Terminates all TCP connections (called after system suspend for example, to speed up recovery) */
+
+ log_info("Closing all remaining TCP connections.");
+
+ bool restart;
+ do {
+ restart = false;
+
+ LIST_FOREACH(streams, s, m->dns_streams) {
+ r = set_ensure_put(&closed, &dns_stream_hash_ops, s);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ /* Haven't seen this one before. Close it. */
+ dns_stream_ref(s);
+ (void) dns_stream_complete(s, ECONNRESET);
+
+ /* This might have a ripple effect, let's hence no look at the list further,
+ * but scan from the beginning again */
+ restart = true;
+ break;
+ }
+ }
+ } while (restart);
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
index ba4a59e..912b9bf 100644
--- a/src/resolve/resolved-dns-stream.h
+++ b/src/resolve/resolved-dns-stream.h
@@ -126,3 +126,4 @@ static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
}
void dns_stream_detach(DnsStream *s);
+int dns_stream_disconnect_all(Manager *m);
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index c59e3b7..10b35da 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -837,12 +837,20 @@ static void dns_stub_query_complete(DnsQuery *query) {
break;
case DNS_TRANSACTION_NO_SERVERS:
+ /* We're not configured to give answers for this question. Refuse it. */
+ (void) dns_stub_send_reply(q, DNS_RCODE_REFUSED);
+ break;
+
+ case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
+ /* This RR Type is not implemented */
+ (void) dns_stub_send_reply(q, DNS_RCODE_NOTIMP);
+ break;
+
case DNS_TRANSACTION_INVALID_REPLY:
case DNS_TRANSACTION_ERRNO:
case DNS_TRANSACTION_ABORTED:
case DNS_TRANSACTION_DNSSEC_FAILED:
case DNS_TRANSACTION_NO_TRUST_ANCHOR:
- case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
case DNS_TRANSACTION_NO_SOURCE:
case DNS_TRANSACTION_STUB_LOOP:
diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c
index 5bde29c..6144dc0 100644
--- a/src/resolve/resolved-dns-synthesize.c
+++ b/src/resolve/resolved-dns-synthesize.c
@@ -463,7 +463,7 @@ int dns_synthesize_answer(
name = dns_resource_key_name(key);
- if (dns_name_is_root(name)) {
+ if (dns_name_is_root(name) || dns_name_endswith(name, "resolver.arpa") > 0) {
/* Do nothing. */
} else if (dns_name_dont_resolve(name)) {
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 8ff5653..ad8b88e 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -175,6 +175,9 @@ DnsTransaction* dns_transaction_gc(DnsTransaction *t) {
if (t->block_gc > 0)
return t;
+ if (t->wait_for_answer && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
+ return t;
+
if (set_isempty(t->notify_query_candidates) &&
set_isempty(t->notify_query_candidates_done) &&
set_isempty(t->notify_zone_items) &&
@@ -2229,7 +2232,7 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource
return 1;
}
-static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
+static int dns_transaction_request_dnssec_rr_full(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) {
_cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
DnsTransaction *aux;
int r;
@@ -2246,13 +2249,18 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
if (r < 0)
return r;
+ if (ret)
+ *ret = NULL;
return 0;
}
/* This didn't work, ask for it via the network/cache then. */
r = dns_transaction_add_dnssec_transaction(t, key, &aux);
- if (r == -ELOOP) /* This would result in a cyclic dependency */
+ if (r == -ELOOP) { /* This would result in a cyclic dependency */
+ if (ret)
+ *ret = NULL;
return 0;
+ }
if (r < 0)
return r;
@@ -2260,11 +2268,19 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
r = dns_transaction_go(aux);
if (r < 0)
return r;
+ if (ret)
+ *ret = aux;
}
return 1;
}
+static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
+ assert(t);
+ assert(key);
+ return dns_transaction_request_dnssec_rr_full(t, key, NULL);
+}
+
static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
int r;
@@ -2365,6 +2381,8 @@ static bool dns_transaction_dnssec_supported_full(DnsTransaction *t) {
int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
DnsResourceRecord *rr;
+ /* Have we already requested a record that would be sufficient to validate an insecure delegation? */
+ bool chased_insecure = false;
int r;
assert(t);
@@ -2377,11 +2395,11 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
* - For RRSIG we get the matching DNSKEY
* - For DNSKEY we get the matching DS
* - For unsigned SOA/NS we get the matching DS
- * - For unsigned CNAME/DNAME/DS we get the parent SOA RR
- * - For other unsigned RRs we get the matching SOA RR
+ * - For unsigned CNAME/DNAME/DS we get the parent DS RR
+ * - For other unsigned RRs we get the matching DS RR
* - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR
- * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
- * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
+ * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's DS RR
+ * - For other queries with no matching response RRs, and no NSEC/NSEC3, the DS RR
*/
if (FLAGS_SET(t->query_flags, SD_RESOLVED_NO_VALIDATE) || t->scope->dnssec_mode == DNSSEC_NO)
@@ -2408,6 +2426,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
case DNS_TYPE_RRSIG: {
/* For each RRSIG we request the matching DNSKEY */
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *dnskey = NULL;
+ DnsTransaction *aux;
/* If this RRSIG is about a DNSKEY RR and the
* signer is the same as the owner, then we
@@ -2444,9 +2463,22 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").",
t->id, dns_resource_key_name(rr->key), rr->rrsig.key_tag);
- r = dns_transaction_request_dnssec_rr(t, dnskey);
+ r = dns_transaction_request_dnssec_rr_full(t, dnskey, &aux);
if (r < 0)
return r;
+
+ /* If we are requesting a DNSKEY, we can anticiapte that we will want the matching DS
+ * in the near future. Let's request it in advance so we don't have to wait in the
+ * common case. */
+ if (aux) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds =
+ dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(dnskey));
+ if (!ds)
+ return -ENOMEM;
+ r = dns_transaction_request_dnssec_rr(t, ds);
+ if (r < 0)
+ return r;
+ }
break;
}
@@ -2521,6 +2553,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r > 0)
continue;
+ chased_insecure = true;
ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key));
if (!ds)
return -ENOMEM;
@@ -2537,11 +2570,11 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
case DNS_TYPE_DS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAME: {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
const char *name;
/* CNAMEs and DNAMEs cannot be located at a
- * zone apex, hence ask for the parent SOA for
+ * zone apex, hence ask for the parent DS for
* unsigned CNAME/DNAME RRs, maybe that's the
* apex. But do all that only if this is
* actually a response to our original
@@ -2575,13 +2608,13 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r == 0)
continue;
- soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, name);
- if (!soa)
+ ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, name);
+ if (!ds)
return -ENOMEM;
- log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).",
+ log_debug("Requesting parent DS to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).",
t->id, dns_resource_key_name(rr->key));
- r = dns_transaction_request_dnssec_rr(t, soa);
+ r = dns_transaction_request_dnssec_rr(t, ds);
if (r < 0)
return r;
@@ -2589,11 +2622,11 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
}
default: {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
/* For other unsigned RRsets (including
* NSEC/NSEC3!), look for proof the zone is
- * unsigned, by requesting the SOA RR of the
+ * unsigned, by requesting the DS RR of the
* zone. However, do so only if they are
* directly relevant to our original
* question. */
@@ -2610,13 +2643,13 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r > 0)
continue;
- soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, dns_resource_key_name(rr->key));
- if (!soa)
+ ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key));
+ if (!ds)
return -ENOMEM;
- log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).",
+ log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).",
t->id, dns_resource_key_name(rr->key), dns_resource_record_to_string(rr));
- r = dns_transaction_request_dnssec_rr(t, soa);
+ r = dns_transaction_request_dnssec_rr(t, ds);
if (r < 0)
return r;
break;
@@ -2631,49 +2664,38 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r < 0)
return r;
if (r > 0) {
- const char *name, *signed_status;
- uint16_t type = 0;
+ const char *name = dns_resource_key_name(dns_transaction_key(t));
+ bool was_signed = dns_answer_contains_nsec_or_nsec3(t->answer);
- name = dns_resource_key_name(dns_transaction_key(t));
- signed_status = dns_answer_contains_nsec_or_nsec3(t->answer) ? "signed" : "unsigned";
-
- /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this
- * could also be used as indication that we are not at a zone apex, but in real world setups there are
- * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even
- * though they have further children. If this was a DS request, then it's signed when the parent zone
- * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR,
- * to see if that is signed. */
-
- if (dns_transaction_key(t)->type == DNS_TYPE_DS) {
- r = dns_name_parent(&name);
- if (r > 0) {
- type = DNS_TYPE_SOA;
- log_debug("Requesting parent SOA (%s %s) to validate transaction %" PRIu16 " (%s, %s empty DS response).",
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id,
- dns_resource_key_name(dns_transaction_key(t)), signed_status);
- } else
+ /* If the response is empty, seek the DS for this name, just in case we're at a zone cut
+ * already, unless we just requested the DS, in which case we have to ask the parent to make
+ * progress.
+ *
+ * If this was an SOA or NS request, we could also skip to the parent, but in real world
+ * setups there are too many broken DNS servers (Hello, incapdns.net!) where non-terminal
+ * zones return NXDOMAIN even though they have further children. */
+
+ if (chased_insecure || was_signed)
+ /* In this case we already reqeusted what we need above. */
+ name = NULL;
+ else if (dns_transaction_key(t)->type == DNS_TYPE_DS)
+ /* If the DS response is empty, we'll walk up the dns labels requesting DS until we
+ * find a referral to the SOA or hit it anyway and get a positive DS response. */
+ if (dns_name_parent(&name) <= 0)
name = NULL;
- } else if (IN_SET(dns_transaction_key(t)->type, DNS_TYPE_SOA, DNS_TYPE_NS)) {
-
- type = DNS_TYPE_DS;
- log_debug("Requesting DS (%s %s) to validate transaction %" PRIu16 " (%s, %s empty SOA/NS response).",
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id, name, signed_status);
-
- } else {
- type = DNS_TYPE_SOA;
- log_debug("Requesting SOA (%s %s) to validate transaction %" PRIu16 " (%s, %s empty non-SOA/NS/DS response).",
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id, name, signed_status);
- }
-
if (name) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
+
+ log_debug("Requesting DS (%s %s) to validate transaction %" PRIu16 " (%s empty response).",
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id,
+ dns_resource_key_name(dns_transaction_key(t)));
- soa = dns_resource_key_new(dns_transaction_key(t)->class, type, name);
- if (!soa)
+ ds = dns_resource_key_new(dns_transaction_key(t)->class, DNS_TYPE_DS, name);
+ if (!ds)
return -ENOMEM;
- r = dns_transaction_request_dnssec_rr(t, soa);
+ r = dns_transaction_request_dnssec_rr(t, ds);
if (r < 0)
return r;
}
@@ -2753,7 +2775,6 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
DnsTransaction *dt;
/* For SOA or NS RRs we look for a matching DS transaction */
-
SET_FOREACH(dt, t->dnssec_transactions) {
if (dns_transaction_key(dt)->class != rr->key->class)
@@ -2761,7 +2782,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), dns_resource_key_name(rr->key));
+ r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
@@ -2790,16 +2811,16 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
DnsTransaction *dt;
/*
- * CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA.
+ * CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent DS.
*
- * DS RRs are signed if the parent is signed, hence also look at the parent SOA
+ * DS RRs are signed if the parent is signed, hence also look at the parent DS
*/
SET_FOREACH(dt, t->dnssec_transactions) {
if (dns_transaction_key(dt)->class != rr->key->class)
continue;
- if (dns_transaction_key(dt)->type != DNS_TYPE_SOA)
+ if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
if (!parent) {
@@ -2817,7 +2838,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
}
}
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), parent);
+ r = dns_name_endswith(parent, dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
@@ -2832,25 +2853,26 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
default: {
DnsTransaction *dt;
- /* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our SOA lookup was authenticated */
+ /* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our DS lookup was authenticated */
SET_FOREACH(dt, t->dnssec_transactions) {
-
if (dns_transaction_key(dt)->class != rr->key->class)
continue;
- if (dns_transaction_key(dt)->type != DNS_TYPE_SOA)
+ if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), dns_resource_key_name(rr->key));
+ r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
continue;
- /* We found the transaction that was supposed to find the SOA RR for us. It was
- * successful, but found no RR for us. This means we are not at a zone cut. In this
- * case, we require authentication if the SOA lookup was authenticated too. */
- return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ if (!FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
+ return false;
+
+ /* We expect this to be signed when the DS record exists, and don't expect it to be
+ * signed when the DS record is proven not to exist. */
+ return dns_answer_match_key(dt->answer, dns_transaction_key(dt), NULL);
}
return true;
@@ -2920,7 +2942,6 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
char key_str[DNS_RESOURCE_KEY_STRING_MAX];
DnsTransaction *dt;
const char *name;
- uint16_t type = 0;
int r;
assert(t);
@@ -2955,43 +2976,37 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
name = dns_resource_key_name(dns_transaction_key(t));
- if (dns_transaction_key(t)->type == DNS_TYPE_DS) {
-
- /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed,
- * hence check the parent SOA in this case. */
-
+ if (IN_SET(dns_transaction_key(t)->type, DNS_TYPE_DS, DNS_TYPE_CNAME, DNS_TYPE_DNAME)) {
+ /* We got a negative reply for this DS/CNAME/DNAME lookup? Check the parent in this case to
+ * see if this answer should have been signed. */
r = dns_name_parent(&name);
if (r < 0)
return r;
if (r == 0)
return true;
+ }
- type = DNS_TYPE_SOA;
-
- } else if (IN_SET(dns_transaction_key(t)->type, DNS_TYPE_SOA, DNS_TYPE_NS))
- /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */
- type = DNS_TYPE_DS;
- else
- /* For all other negative replies, check for the SOA lookup */
- type = DNS_TYPE_SOA;
-
- /* For all other RRs we check the SOA on the same level to see
+ /* For all other RRs we check the DS on the same level to see
* if it's signed. */
SET_FOREACH(dt, t->dnssec_transactions) {
-
if (dns_transaction_key(dt)->class != dns_transaction_key(t)->class)
continue;
- if (dns_transaction_key(dt)->type != type)
+ if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), name);
+ r = dns_name_endswith(name, dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
continue;
- return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ if (!FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
+ return false;
+
+ /* We expect this to be signed when the DS record exists, and don't expect it to be signed
+ * when the DS record is proven not to exist. */
+ return dns_answer_match_key(dt->answer, dns_transaction_key(dt), NULL);
}
/* If in doubt, require NSEC/NSEC3 */
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 2fd8720..6be7c5f 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -134,6 +134,11 @@ struct DnsTransaction {
unsigned block_gc;
+ /* Set when we're willing to let this transaction live beyond it's usefulness for the original query,
+ * for caching purposes. This blocks gc while there is still a chance we might still receive an
+ * answer. */
+ bool wait_for_answer;
+
LIST_FIELDS(DnsTransaction, transactions_by_scope);
LIST_FIELDS(DnsTransaction, transactions_by_stream);
LIST_FIELDS(DnsTransaction, transactions_by_key);
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index 1703c43..8aea5e1 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -165,6 +165,11 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
/* Defined by RFC 8375. The most official choice. */
"home.arpa\0"
+ /* RFC 9462 doesn't mention DNSSEC, but this domain
+ * can't really be signed and clients need to validate
+ * the answer before using it anyway. */
+ "resolver.arpa\0"
+
/* RFC 8880 says because the 'ipv4only.arpa' zone has to
* be an insecure delegation, DNSSEC cannot be used to
* protect these answers from tampering by malicious
diff --git a/src/rpm/macros.systemd.in b/src/rpm/macros.systemd.in
index 241e4b9..317e13d 100644
--- a/src/rpm/macros.systemd.in
+++ b/src/rpm/macros.systemd.in
@@ -13,6 +13,7 @@
%_udevhwdbdir {{UDEV_HWDB_DIR}}
%_udevrulesdir {{UDEV_RULES_DIR}}
%_journalcatalogdir {{SYSTEMD_CATALOG_DIR}}
+%_kernel_install_dir {{KERNEL_INSTALL_DIR}}
%_binfmtdir {{BINFMT_DIR}}
%_sysctldir {{SYSCTL_DIR}}
%_sysusersdir {{SYSUSERS_DIR}}
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 569ef46..a4e2dae 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -120,13 +120,13 @@ static const BaseFilesystem table[] = {
# else
# error "Unknown RISC-V ABI"
# endif
-#elif defined(__s390__)
- /* s390-linux-gnu */
#elif defined(__s390x__)
{ "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
"usr/lib64\0"
"usr/lib\0", "ld-lsb-s390x.so.3" },
# define KNOW_LIB64_DIRS 1
+#elif defined(__s390__)
+ /* s390-linux-gnu */
#elif defined(__sparc__)
#endif
/* gcc doesn't allow pragma to be used within constructs, hence log about this separately below */
diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c
index c906aec..7a2dd1c 100644
--- a/src/shared/blockdev-util.c
+++ b/src/shared/blockdev-util.c
@@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "device-private.h"
#include "device-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
@@ -367,24 +368,36 @@ int lock_whole_block_device(dev_t devt, int operation) {
}
int blockdev_partscan_enabled(int fd) {
- _cleanup_free_ char *p = NULL, *buf = NULL;
- unsigned long long ull;
- struct stat st;
- int r;
-
- /* Checks if partition scanning is correctly enabled on the block device */
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ unsigned capability;
+ int r, ext_range;
- if (fstat(fd, &st) < 0)
- return -errno;
+ /* Checks if partition scanning is correctly enabled on the block device.
+ *
+ * The 'GENHD_FL_NO_PART_SCAN' flag was introduced by
+ * https://github.com/torvalds/linux/commit/d27769ec3df1a8de9ca450d2dcd72d1ab259ba32 (v3.2).
+ * But at that time, the flag is also effectively implied when 'minors' element of 'struct gendisk'
+ * is 1, which can be check with 'ext_range' sysfs attribute. Explicit flag ('GENHD_FL_NO_PART_SCAN')
+ * can be obtained from 'capability' sysattr.
+ *
+ * With https://github.com/torvalds/linux/commit/1ebe2e5f9d68e94c524aba876f27b945669a7879 (v5.17), we
+ * can check the flag from 'ext_range' sysfs attribute directly.
+ *
+ * With https://github.com/torvalds/linux/commit/e81cd5a983bb35dabd38ee472cf3fea1c63e0f23 (v6.3),
+ * the 'capability' sysfs attribute is deprecated, hence we cannot check the flag from it.
+ *
+ * To support both old and new kernels, we need to do the following: first check 'ext_range' sysfs
+ * attribute, and if '1' we can conclude partition scanning is disabled, otherwise check 'capability'
+ * sysattr for older version. */
- if (!S_ISBLK(st.st_mode))
- return -ENOTBLK;
+ assert(fd >= 0);
- if (asprintf(&p, "/sys/dev/block/%u:%u/capability", major(st.st_rdev), minor(st.st_rdev)) < 0)
- return -ENOMEM;
+ r = block_device_new_from_fd(fd, 0, &dev);
+ if (r < 0)
+ return r;
- r = read_one_line_file(p, &buf);
- if (r == -ENOENT) /* If the capability file doesn't exist then we are most likely looking at a
+ r = device_get_sysattr_int(dev, "ext_range", &ext_range);
+ if (r == -ENOENT) /* If the ext_range file doesn't exist then we are most likely looking at a
* partition block device, not the whole block device. And that means we have no
* partition scanning on for it (we do for its parent, but not for the partition
* itself). */
@@ -392,7 +405,13 @@ int blockdev_partscan_enabled(int fd) {
if (r < 0)
return r;
- r = safe_atollu_full(buf, 16, &ull);
+ if (ext_range <= 1) /* The valus should be always positive, but the kernel uses '%d' for the
+ * attribute. Let's gracefully handle zero or negative. */
+ return false;
+
+ r = device_get_sysattr_unsigned_full(dev, "capability", 16, &capability);
+ if (r == -ENOENT)
+ return false;
if (r < 0)
return r;
@@ -400,7 +419,12 @@ int blockdev_partscan_enabled(int fd) {
#define GENHD_FL_NO_PART_SCAN (0x0200)
#endif
- return !FLAGS_SET(ull, GENHD_FL_NO_PART_SCAN);
+ /* If 0x200 is set, part scanning is definitely off. */
+ if (FLAGS_SET(capability, GENHD_FL_NO_PART_SCAN))
+ return false;
+
+ /* Otherwise, assume part scanning is on, we have no further checks available. Assume the best. */
+ return true;
}
static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c
index 15301ae..f00dbea 100644
--- a/src/shared/bpf-dlopen.c
+++ b/src/shared/bpf-dlopen.c
@@ -74,18 +74,23 @@ int dlopen_bpf(void) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"neither libbpf.so.1 nor libbpf.so.0 are installed: %s", dlerror());
+ log_debug("Loaded 'libbpf.so.0' via dlopen()");
+
/* symbols deprecated in 1.0 we use as compat */
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
#if MODERN_LIBBPF
/* Don't exist anymore in new libbpf, hence cannot type check them */
DLSYM_ARG_FORCE(bpf_create_map),
- DLSYM_ARG_FORCE(bpf_probe_prog_type));
+ DLSYM_ARG_FORCE(bpf_probe_prog_type)
#else
DLSYM_ARG(bpf_create_map),
- DLSYM_ARG(bpf_probe_prog_type));
+ DLSYM_ARG(bpf_probe_prog_type)
#endif
+ );
} else {
+ log_debug("Loaded 'libbpf.so.1' via dlopen()");
+
/* symbols available from 0.7.0 */
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
@@ -99,6 +104,8 @@ int dlopen_bpf(void) {
#endif
);
}
+ if (r < 0)
+ return r;
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index b3e4b50..2ed6bf2 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -65,7 +65,7 @@ int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- fd = xopenat(dir_fd, path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (fd < 0)
return fd;
@@ -113,7 +113,7 @@ int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret) {
assert(path);
assert(ret);
- fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
return fd;
@@ -1276,8 +1276,6 @@ static int subvol_snapshot_children(
if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
subvolume_fd = xopenat_lock(new_fd, subvolume,
O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
- /* xopen_flags = */ 0,
- /* mode = */ 0,
LOCK_BSD,
LOCK_EX);
if (subvolume_fd < 0)
@@ -1445,7 +1443,7 @@ int btrfs_subvol_snapshot_at_full(
assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
assert(to);
- old_fd = xopenat(dir_fdf, from, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0);
+ old_fd = xopenat(dir_fdf, from, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (old_fd < 0)
return old_fd;
@@ -1482,8 +1480,6 @@ int btrfs_subvol_snapshot_at_full(
if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
subvolume_fd = xopenat_lock(new_fd, subvolume,
O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
- /* xopen_flags = */ 0,
- /* mode = */ 0,
LOCK_BSD,
LOCK_EX);
if (subvolume_fd < 0)
diff --git a/src/shared/copy.c b/src/shared/copy.c
index c0e30cd..2b87cba 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -208,6 +208,7 @@ int copy_bytes_full(
r = reflink_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
if (r >= 0) {
off_t t;
+ int ret;
/* This worked, yay! Now — to be fully correct — let's adjust the file pointers */
if (max_bytes == UINT64_MAX) {
@@ -226,7 +227,14 @@ int copy_bytes_full(
if (t < 0)
return -errno;
- return 0; /* we copied the whole thing, hence hit EOF, return 0 */
+ if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+ r = fd_verify_linked(fdf);
+ if (r < 0)
+ return r;
+ }
+
+ /* We copied the whole thing, hence hit EOF, return 0. */
+ ret = 0;
} else {
t = lseek(fdf, foffset + max_bytes, SEEK_SET);
if (t < 0)
@@ -236,8 +244,18 @@ int copy_bytes_full(
if (t < 0)
return -errno;
- return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */
+ /* We copied only some number of bytes, which worked, but
+ * this means we didn't hit EOF, return 1. */
+ ret = 1;
+ }
+
+ if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+ r = fd_verify_linked(fdf);
+ if (r < 0)
+ return r;
}
+
+ return ret;
}
}
}
@@ -316,7 +334,7 @@ int copy_bytes_full(
if (try_cfr) {
n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
if (n < 0) {
- if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF))
+ if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF, -EOPNOTSUPP))
return n;
try_cfr = false;
@@ -483,6 +501,12 @@ int copy_bytes_full(
copied_something = true;
}
+ if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+ r = fd_verify_linked(fdf);
+ if (r < 0)
+ return r;
+ }
+
if (copy_flags & COPY_TRUNCATE) {
off_t off = lseek(fdt, 0, SEEK_CUR);
if (off < 0)
@@ -508,7 +532,6 @@ static int fd_copy_symlink(
_cleanup_free_ char *target = NULL;
int r;
- assert(from);
assert(st);
assert(to);
@@ -526,7 +549,10 @@ static int fd_copy_symlink(
mac_selinux_create_file_clear();
if (r < 0) {
if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
- log_notice_errno(r, "Failed to copy symlink '%s', ignoring: %m", from);
+ log_notice_errno(r, "Failed to copy symlink%s%s%s, ignoring: %m",
+ isempty(from) ? "" : " '",
+ strempty(from),
+ isempty(from) ? "" : "'");
return 0;
}
@@ -757,7 +783,6 @@ static int fd_copy_regular(
_cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
int r, q;
- assert(from);
assert(st);
assert(to);
@@ -767,9 +792,9 @@ static int fd_copy_regular(
if (r > 0) /* worked! */
return 0;
- fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ fdf = xopenat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fdf < 0)
- return -errno;
+ return fdf;
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare_at(dt, to, S_IFREG);
@@ -797,6 +822,12 @@ static int fd_copy_regular(
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
+ if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+ r = fd_verify_linked(fdf);
+ if (r < 0)
+ return r;
+ }
+
if (copy_flags & COPY_FSYNC) {
if (fsync(fdt) < 0) {
r = -errno;
@@ -830,7 +861,6 @@ static int fd_copy_fifo(
HardlinkContext *hardlink_context) {
int r;
- assert(from);
assert(st);
assert(to);
@@ -849,7 +879,10 @@ static int fd_copy_fifo(
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) {
- log_notice_errno(r, "Failed to copy fifo '%s', ignoring: %m", from);
+ log_notice_errno(r, "Failed to copy fifo%s%s%s, ignoring: %m",
+ isempty(from) ? "" : " '",
+ strempty(from),
+ isempty(from) ? "" : "'");
return 0;
} else if (r < 0)
return r;
@@ -881,7 +914,6 @@ static int fd_copy_node(
HardlinkContext *hardlink_context) {
int r;
- assert(from);
assert(st);
assert(to);
@@ -900,7 +932,10 @@ static int fd_copy_node(
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) {
- log_notice_errno(r, "Failed to copy node '%s', ignoring: %m", from);
+ log_notice_errno(r, "Failed to copy node%s%s%s, ignoring: %m",
+ isempty(from) ? "" : " '",
+ strempty(from),
+ isempty(from) ? "" : "'");
return 0;
} else if (r < 0)
return r;
@@ -955,12 +990,9 @@ static int fd_copy_directory(
if (depth_left == 0)
return -ENAMETOOLONG;
- if (from)
- fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- else
- fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
+ fdf = xopenat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fdf < 0)
- return -errno;
+ return fdf;
if (!hardlink_context) {
/* If recreating hardlinks is requested let's set up a context for that now. */
@@ -984,19 +1016,19 @@ static int fd_copy_directory(
exists = r >= 0;
- fdt = xopenat_lock(dt, to,
- O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|(exists ? 0 : O_CREAT|O_EXCL),
- (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0)|(set_contains(subvolumes, st) ? XO_SUBVOLUME : 0),
- st->st_mode & 07777,
- copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE,
- LOCK_EX);
+ fdt = xopenat_lock_full(dt, to,
+ O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|(exists ? 0 : O_CREAT|O_EXCL),
+ (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0)|(set_contains(subvolumes, st) ? XO_SUBVOLUME : 0),
+ st->st_mode & 07777,
+ copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE,
+ LOCK_EX);
if (fdt < 0)
return fdt;
r = 0;
if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) {
- log_debug("%s is in the denylist, not recursing", from);
+ log_debug("%s is in the denylist, not recursing", from ?: "file to copy");
goto finish;
}
@@ -1030,7 +1062,8 @@ static int fd_copy_directory(
}
if (PTR_TO_INT(hashmap_get(denylist, &buf)) == DENY_INODE) {
- log_debug("%s/%s is in the denylist, ignoring", from, de->d_name);
+ log_debug("%s%s%s is in the denylist, ignoring",
+ strempty(from), isempty(from) ? "" : "/", de->d_name);
continue;
}
@@ -1163,10 +1196,10 @@ static int fd_copy_tree_generic(
DenyType t = PTR_TO_INT(hashmap_get(denylist, st));
if (t == DENY_INODE) {
- log_debug("%s is in the denylist, ignoring", from);
+ log_debug("%s is in the denylist, ignoring", from ?: "file to copy");
return 0;
} else if (t == DENY_CONTENTS)
- log_debug("%s is configured to have its contents excluded, but is not a directory", from);
+ log_debug("%s is configured to have its contents excluded, but is not a directory", from ?: "file to copy");
r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
/* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */
@@ -1198,11 +1231,10 @@ int copy_tree_at_full(
struct stat st;
int r;
- assert(from);
assert(to);
assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
- if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fstatat(fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW | (isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
return -errno;
r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid,
@@ -1305,13 +1337,12 @@ int copy_file_fd_at_full(
int r;
assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
- assert(from);
assert(fdt >= 0);
assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
- fdf = openat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ fdf = xopenat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fdf < 0)
- return -errno;
+ return fdf;
r = fd_verify_regular(fdf);
if (r < 0)
@@ -1332,6 +1363,12 @@ int copy_file_fd_at_full(
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
}
+ if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+ r = fd_verify_linked(fdf);
+ if (r < 0)
+ return r;
+ }
+
if (copy_flags & COPY_FSYNC_FULL) {
r = fsync_full(fdt);
if (r < 0)
@@ -1363,12 +1400,11 @@ int copy_file_at_full(
assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
- assert(from);
assert(to);
- fdf = openat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ fdf = xopenat(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fdf < 0)
- return -errno;
+ return fdf;
if (fstat(fdf, &st) < 0)
return -errno;
@@ -1378,11 +1414,11 @@ int copy_file_at_full(
return r;
WITH_UMASK(0000) {
- fdt = xopenat_lock(dir_fdt, to,
- flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
- (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0),
- mode != MODE_INVALID ? mode : st.st_mode,
- copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, LOCK_EX);
+ fdt = xopenat_lock_full(dir_fdt, to,
+ flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
+ (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0),
+ mode != MODE_INVALID ? mode : st.st_mode,
+ copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, LOCK_EX);
if (fdt < 0)
return fdt;
}
@@ -1403,6 +1439,12 @@ int copy_file_at_full(
(void) copy_times(fdf, fdt, copy_flags);
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
+ if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+ r = fd_verify_linked(fdf);
+ if (r < 0)
+ goto fail;
+ }
+
if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
@@ -1451,7 +1493,6 @@ int copy_file_atomic_at_full(
_cleanup_close_ int fdt = -EBADF;
int r;
- assert(from);
assert(to);
assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
diff --git a/src/shared/copy.h b/src/shared/copy.h
index d842edd..b8fb28a 100644
--- a/src/shared/copy.h
+++ b/src/shared/copy.h
@@ -30,6 +30,7 @@ typedef enum CopyFlags {
COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
+ COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
} CopyFlags;
typedef enum DenyType {
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
index 7cc8889..fa8ebe0 100644
--- a/src/shared/creds-util.c
+++ b/src/shared/creds-util.c
@@ -826,9 +826,9 @@ int encrypt_credential_and_warn(
tpm2_pubkey_pcr_mask = 0;
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
- r = tpm2_context_new(tpm2_device, &tpm2_context);
+ r = tpm2_context_new_or_warn(tpm2_device, &tpm2_context);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
r = tpm2_get_best_pcr_bank(tpm2_context, tpm2_hash_pcr_mask | tpm2_pubkey_pcr_mask, &tpm2_pcr_bank);
if (r < 0)
diff --git a/src/shared/data-fd-util.h b/src/shared/data-fd-util.h
index 4f3d8b8..6d99209 100644
--- a/src/shared/data-fd-util.h
+++ b/src/shared/data-fd-util.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <inttypes.h>
+#include <stddef.h>
enum {
ACQUIRE_NO_DEV_NULL = 1 << 0,
diff --git a/src/shared/dlfcn-util.c b/src/shared/dlfcn-util.c
index a321df3..8022f55 100644
--- a/src/shared/dlfcn-util.c
+++ b/src/shared/dlfcn-util.c
@@ -49,6 +49,8 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"%s is not installed: %s", filename, dlerror());
+ log_debug("Loaded '%s' via dlopen()", filename);
+
va_list ap;
va_start(ap, log_level);
r = dlsym_many_or_warnv(dl, log_level, ap);
diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c
index 0d215e8..c3991cf 100644
--- a/src/shared/hibernate-util.c
+++ b/src/shared/hibernate-util.c
@@ -23,6 +23,7 @@
#include "log.h"
#include "parse-util.h"
#include "path-util.h"
+#include "proc-cmdline.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@@ -129,6 +130,13 @@ static int read_resume_config(dev_t *ret_devno, uint64_t *ret_offset) {
assert(ret_devno);
assert(ret_offset);
+ r = proc_cmdline_get_key("noresume", /* flags = */ 0, /* ret_value = */ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if 'noresume' kernel command line option is set: %m");
+ if (r > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "'noresume' kernel command line option is set, refusing hibernation device lookup.");
+
r = read_one_line_file("/sys/power/resume", &devno_str);
if (r < 0)
return log_debug_errno(r, "Failed to read /sys/power/resume: %m");
diff --git a/src/shared/idn-util.c b/src/shared/idn-util.c
index 6f36688..26a9d60 100644
--- a/src/shared/idn-util.c
+++ b/src/shared/idn-util.c
@@ -50,7 +50,10 @@ int dlopen_idn(void) {
if (!dl)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"libidn support is not installed: %s", dlerror());
- }
+ log_debug("Loaded 'libidn.so.11' via dlopen()");
+ } else
+ log_debug("Loaded 'libidn.so.12' via dlopen()");
+
r = dlsym_many_or_warn(
dl,
diff --git a/src/shared/install.c b/src/shared/install.c
index 0f4dab4..27a421e 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -340,9 +340,12 @@ void install_changes_dump(int r, const char *verb, const InstallChange *changes,
assert(verb || r >= 0);
for (size_t i = 0; i < n_changes; i++) {
- if (changes[i].type < 0)
- assert(verb);
assert(changes[i].path);
+ /* This tries to tell the compiler that it's safe to use 'verb' in a string format if there
+ * was an error, but the compiler doesn't care and fails anyway, so strna(verb) is used
+ * too. */
+ assert(verb || changes[i].type >= 0);
+ verb = strna(verb);
/* When making changes here, make sure to also change install_error() in dbus-manager.c. */
diff --git a/src/shared/journal-file-util.c b/src/shared/journal-file-util.c
index e444a2b..bdceac4 100644
--- a/src/shared/journal-file-util.c
+++ b/src/shared/journal-file-util.c
@@ -210,11 +210,16 @@ static void journal_file_set_offline_internal(JournalFile *f) {
log_debug_errno(r, "Failed to re-enable copy-on-write for %s: %m, rewriting file", f->path);
- r = copy_file_atomic_full(FORMAT_PROC_FD_PATH(f->fd), f->path, f->mode,
- 0,
- FS_NOCOW_FL,
- COPY_REPLACE | COPY_FSYNC | COPY_HOLES | COPY_ALL_XATTRS,
- NULL, NULL);
+ /* Here, setting COPY_VERIFY_LINKED flag is crucial. Otherwise, a broken
+ * journal file may be created, if journal_directory_vacuum() ->
+ * unlinkat_deallocate() is called in the main thread while this thread is
+ * copying the file. See issue #24150 and #31222. */
+ r = copy_file_atomic_at_full(
+ f->fd, NULL, AT_FDCWD, f->path, f->mode,
+ 0,
+ FS_NOCOW_FL,
+ COPY_REPLACE | COPY_FSYNC | COPY_HOLES | COPY_ALL_XATTRS | COPY_VERIFY_LINKED,
+ NULL, NULL);
if (r < 0) {
log_debug_errno(r, "Failed to rewrite %s: %m", f->path);
continue;
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index a5d0400..0a31be3 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -2088,7 +2088,7 @@ int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) {
if (sd_id128_equal(i->id, boot.id))
/* The boot id is already stored, something wrong with the journal files.
* Exiting as otherwise this problem would cause an infinite loop. */
- break;
+ goto finish;
if (!GREEDY_REALLOC(boots, n_boots + 1))
return -ENOMEM;
@@ -2096,6 +2096,7 @@ int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) {
boots[n_boots++] = boot;
}
+ finish:
*ret_boots = TAKE_PTR(boots);
*ret_n_boots = n_boots;
return n_boots > 0;
diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c
index 5860303..6d55df7 100644
--- a/src/shared/loop-util.c
+++ b/src/shared/loop-util.c
@@ -702,9 +702,9 @@ int loop_device_make_by_path_at(
direct_flags = FLAGS_SET(loop_flags, LO_FLAGS_DIRECT_IO) ? O_DIRECT : 0;
rdwr_flags = open_flags >= 0 ? open_flags : O_RDWR;
- fd = xopenat(dir_fd, path, basic_flags|direct_flags|rdwr_flags, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|direct_flags|rdwr_flags);
if (fd < 0 && direct_flags != 0) /* If we had O_DIRECT on, and things failed with that, let's immediately try again without */
- fd = xopenat(dir_fd, path, basic_flags|rdwr_flags, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|rdwr_flags);
else
direct = direct_flags != 0;
if (fd < 0) {
@@ -714,9 +714,9 @@ int loop_device_make_by_path_at(
if (open_flags >= 0 || !(ERRNO_IS_PRIVILEGE(r) || r == -EROFS))
return r;
- fd = xopenat(dir_fd, path, basic_flags|direct_flags|O_RDONLY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|direct_flags|O_RDONLY);
if (fd < 0 && direct_flags != 0) /* as above */
- fd = xopenat(dir_fd, path, basic_flags|O_RDONLY, /* xopen_flags = */ 0, /* mode = */ 0);
+ fd = xopenat(dir_fd, path, basic_flags|O_RDONLY);
else
direct = direct_flags != 0;
if (fd < 0)
diff --git a/src/shared/open-file.c b/src/shared/open-file.c
index 42772bd..7d7a8a9 100644
--- a/src/shared/open-file.c
+++ b/src/shared/open-file.c
@@ -96,7 +96,7 @@ int open_file_to_string(const OpenFile *of, char **ret) {
assert(of);
assert(ret);
- s = shell_escape(of->path, ":");
+ s = xescape(of->path, ":");
if (!s)
return -ENOMEM;
diff --git a/src/shared/serialize.c b/src/shared/serialize.c
index 483cbc7..344b102 100644
--- a/src/shared/serialize.c
+++ b/src/shared/serialize.c
@@ -180,7 +180,7 @@ int serialize_strv(FILE *f, const char *key, char **l) {
}
int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
- int copy;
+ int r;
assert(f);
assert(fds);
@@ -188,17 +188,23 @@ int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
if (!pidref_is_set(pidref))
return 0;
- /* If we have a pidfd we serialize the fd and encode the fd number prefixed by "@" in the
- * serialization. Otherwise we serialize the numeric PID as it is. */
+ /* We always serialize the pid separately, to keep downgrades mostly working (older versions will
+ * deserialize the pid and silently fail to deserialize the pidfd). If we also have a pidfd, we
+ * serialize both the pid and pidfd, so that we can construct the exact same pidref after
+ * deserialization (this doesn't work with only the pidfd, as we can't retrieve the original pid
+ * from the pidfd anymore if the process is reaped). */
- if (pidref->fd < 0)
- return serialize_item_format(f, key, PID_FMT, pidref->pid);
+ if (pidref->fd >= 0) {
+ int copy = fdset_put_dup(fds, pidref->fd);
+ if (copy < 0)
+ return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
- copy = fdset_put_dup(fds, pidref->fd);
- if (copy < 0)
- return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
+ r = serialize_item_format(f, key, "@%i:" PID_FMT, copy, pidref->pid);
+ if (r < 0)
+ return r;
+ }
- return serialize_item_format(f, key, "@%i", copy);
+ return serialize_item_format(f, key, PID_FMT, pidref->pid);
}
int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
@@ -476,12 +482,39 @@ int deserialize_pidref(FDSet *fds, const char *value, PidRef *ret) {
e = startswith(value, "@");
if (e) {
- int fd = deserialize_fd(fds, e);
+ _cleanup_free_ char *fdstr = NULL, *pidstr = NULL;
+ _cleanup_close_ int fd = -EBADF;
+
+ r = extract_many_words(&e, ":", /* flags = */ 0, &fdstr, &pidstr, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to deserialize pidref '%s': %m", e);
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot deserialize pidref from empty string.");
+
+ assert(r <= 2);
+ fd = deserialize_fd(fds, fdstr);
if (fd < 0)
return fd;
- r = pidref_set_pidfd_consume(ret, fd);
+ /* The serialization format changed after 255.4. In systemd <= 255.4 only pidfd is
+ * serialized, but that causes problems when reconstructing pidref (see serialize_pidref for
+ * details). After 255.4 the pid is serialized as well even if we have a pidfd, but we still
+ * need to support older format as we might be upgrading from a version that still uses the
+ * old format. */
+ if (pidstr) {
+ pid_t pid;
+
+ r = parse_pid(pidstr, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse PID: %s", pidstr);
+
+ *ret = (PidRef) {
+ .pid = pid,
+ .fd = TAKE_FD(fd),
+ };
+ } else
+ r = pidref_set_pidfd_consume(ret, TAKE_FD(fd));
} else {
pid_t pid;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 30b4f57..c7e0b24 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -664,7 +664,9 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
context->tcti_dl = dlopen(fn, RTLD_NOW);
if (!context->tcti_dl)
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to load %s: %s", fn, dlerror());
+
+ log_debug("Loaded '%s' via dlopen()", fn);
func = dlsym(context->tcti_dl, TSS2_TCTI_INFO_SYMBOL);
if (!func)
@@ -678,7 +680,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
log_debug("Loaded TCTI module '%s' (%s) [Version %" PRIu32 "]", info->name, info->description, info->version);
- rc = info->init(NULL, &sz, NULL);
+ rc = info->init(/* context= */ NULL, &sz, /* param= */ NULL);
if (rc != TPM2_RC_SUCCESS)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
@@ -713,19 +715,37 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
/* We require AES and CFB support for session encryption. */
if (!tpm2_supports_alg(context, TPM2_ALG_AES))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support AES.");
if (!tpm2_supports_alg(context, TPM2_ALG_CFB))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support CFB.");
if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support AES-128-CFB.");
*ret_context = TAKE_PTR(context);
return 0;
}
+int tpm2_context_new_or_warn(const char *device, Tpm2Context **ret_context) {
+ int r;
+
+ assert(ret_context);
+
+ r = tpm2_context_new(device, ret_context);
+ if (r == -EOPNOTSUPP)
+ return log_error_errno(r, "TPM device not usable as it does not support the required functionality (AES-128-CFB missing?).");
+ if (r == -ENOPKG)
+ return log_error_errno(r, "TPM TCTI driver not available.");
+ if (r == -ENOENT)
+ return log_error_errno(r, "TPM device not found.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create TPM2 context: %m");
+
+ return 0;
+}
+
static void tpm2_handle_cleanup(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle, bool flush) {
TSS2_RC rc;
@@ -5540,13 +5560,13 @@ int tpm2_unseal(Tpm2Context *c,
if (r < 0)
return r;
- _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
- r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
- if (r < 0)
- return r;
-
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
+ r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
+ if (r < 0)
+ return r;
+
_cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 55d7481..911a3c7 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -72,6 +72,7 @@ typedef struct {
} Tpm2Context;
int tpm2_context_new(const char *device, Tpm2Context **ret_context);
+int tpm2_context_new_or_warn(const char *device, Tpm2Context **ret_context);
Tpm2Context *tpm2_context_ref(Tpm2Context *context);
Tpm2Context *tpm2_context_unref(Tpm2Context *context);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
diff --git a/src/shared/verbs.c b/src/shared/verbs.c
index a010952..a38591d 100644
--- a/src/shared/verbs.c
+++ b/src/shared/verbs.c
@@ -13,22 +13,21 @@
#include "verbs.h"
#include "virt.h"
-/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external
- * processes can reliably force this on.
- */
+/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check
+ * so external processes can reliably force this on. */
bool running_in_chroot_or_offline(void) {
int r;
- /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but
- * not "start"/"restart" for example.
+ /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset",
+ * but not "start"/"restart" for example.
*
* See docs/ENVIRONMENT.md for docs.
*/
r = getenv_bool("SYSTEMD_OFFLINE");
- if (r < 0 && r != -ENXIO)
- log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m");
- else if (r >= 0)
+ if (r >= 0)
return r > 0;
+ if (r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE, ignoring: %m");
/* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's
* "mock", which is used for package builds. We don't want to try to start systemd services there, since
@@ -40,8 +39,7 @@ bool running_in_chroot_or_offline(void) {
*/
r = running_in_chroot();
if (r < 0)
- log_debug_errno(r, "running_in_chroot(): %m");
-
+ log_debug_errno(r, "Failed to check if we're running in chroot, assuming not: %m");
return r > 0;
}
@@ -145,6 +143,17 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command verb '%s'.", name);
}
+ _cleanup_free_ char *verb_list = NULL;
+ size_t i;
+
+ for (i = 0; verbs[i].dispatch; i++)
+ if (!strextend_with_separator(&verb_list, ", ", verbs[i].verb))
+ return log_oom();
+
+ if (i > 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Command verb required (one of %s).", verb_list);
+
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Command verb required.");
}
diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c
index 2d79f71..99ccefb 100644
--- a/src/shared/watchdog.c
+++ b/src/shared/watchdog.c
@@ -95,7 +95,7 @@ static int set_pretimeout_governor(const char *governor) {
governor,
WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
if (r < 0)
- return log_error_errno(r, "Failed to set pretimeout_governor to '%s': %m", governor);
+ return log_error_errno(r, "Failed to set watchdog pretimeout_governor to '%s': %m", governor);
return r;
}
@@ -157,7 +157,7 @@ static int watchdog_read_pretimeout(void) {
if (ioctl(watchdog_fd, WDIOC_GETPRETIMEOUT, &sec) < 0) {
watchdog_pretimeout = 0;
- return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get pretimeout value, ignoring: %m");
+ return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get watchdog pretimeout value, ignoring: %m");
}
watchdog_pretimeout = sec * USEC_PER_SEC;
@@ -181,7 +181,7 @@ static int watchdog_set_pretimeout(void) {
return 0;
}
- return log_error_errno(errno, "Failed to set pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
+ return log_error_errno(errno, "Failed to set watchdog pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
}
/* The set ioctl does not return the actual value set so get it now. */
@@ -274,10 +274,10 @@ static int update_timeout(void) {
r = watchdog_set_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to set timeout to %s: %m",
+ return log_error_errno(r, "Failed to set watchdog hardware timeout to %s: %m",
FORMAT_TIMESPAN(watchdog_timeout, 0));
- log_info("Modifying watchdog timeout is not supported, reusing the programmed timeout.");
+ log_info("Modifying watchdog hardware timeout is not supported, reusing the programmed timeout.");
watchdog_timeout = USEC_INFINITY;
}
}
@@ -286,8 +286,8 @@ static int update_timeout(void) {
r = watchdog_read_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
- log_info("Reading watchdog timeout is not supported, reusing the configured timeout.");
+ return log_error_errno(r, "Failed to query watchdog hardware timeout: %m");
+ log_info("Reading watchdog hardware timeout is not supported, reusing the configured timeout.");
watchdog_timeout = previous_timeout;
}
}
@@ -302,7 +302,7 @@ static int update_timeout(void) {
if (r < 0)
return r;
- log_info("Watchdog running with a timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
+ log_info("Watchdog running with a hardware timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
return watchdog_ping_now();
}
diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c
index 268e528..7f97325 100644
--- a/src/systemctl/systemctl-logind.c
+++ b/src/systemctl/systemctl-logind.c
@@ -392,7 +392,7 @@ int logind_show_shutdown(void) {
return r;
if (isempty(action))
- return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown.");
+ return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown.");
if (STR_IN_SET(action, "halt", "poweroff", "exit"))
pretty_action = "Shutdown";
diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c
index e7fabcf..5d1eb49 100644
--- a/src/systemctl/systemctl-show.c
+++ b/src/systemctl/systemctl-show.c
@@ -2255,7 +2255,7 @@ static int get_unit_dbus_path_by_pid(
* sends the numeric PID. */
pidfd = pidfd_open(pid, 0);
- if (pidfd < 0 && ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
+ if (pidfd < 0 && (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)))
return get_unit_dbus_path_by_pid_fallback(bus, pid, ret_path, ret_unit);
if (pidfd < 0)
return log_error_errno(errno, "Failed to open PID %"PRIu32": %m", pid);
diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h
index 5e80ea8..d06c5c3 100644
--- a/src/systemd/sd-bus-vtable.h
+++ b/src/systemd/sd-bus-vtable.h
@@ -208,6 +208,7 @@ struct sd_bus_vtable {
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
NAME, ...) NAME
+#define _SD_VARARGS_FOREACH_EVEN_00(FN)
#define _SD_VARARGS_FOREACH_EVEN_01(FN, X) FN(X)
#define _SD_VARARGS_FOREACH_EVEN_02(FN, X, Y) FN(X)
#define _SD_VARARGS_FOREACH_EVEN_04(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_02(FN, __VA_ARGS__)
@@ -261,9 +262,11 @@ struct sd_bus_vtable {
_SD_VARARGS_FOREACH_EVEN_08, _SD_VARARGS_FOREACH_EVEN_07, \
_SD_VARARGS_FOREACH_EVEN_06, _SD_VARARGS_FOREACH_EVEN_05, \
_SD_VARARGS_FOREACH_EVEN_04, _SD_VARARGS_FOREACH_EVEN_03, \
- _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01) \
+ _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01, \
+ _SD_VARARGS_FOREACH_EVEN_00) \
(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_ODD_00(FN)
#define _SD_VARARGS_FOREACH_ODD_01(FN, X)
#define _SD_VARARGS_FOREACH_ODD_02(FN, X, Y) FN(Y)
#define _SD_VARARGS_FOREACH_ODD_04(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_02(FN, __VA_ARGS__)
@@ -317,7 +320,8 @@ struct sd_bus_vtable {
_SD_VARARGS_FOREACH_ODD_08, _SD_VARARGS_FOREACH_ODD_07, \
_SD_VARARGS_FOREACH_ODD_06, _SD_VARARGS_FOREACH_ODD_05, \
_SD_VARARGS_FOREACH_ODD_04, _SD_VARARGS_FOREACH_ODD_03, \
- _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01) \
+ _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01, \
+ _SD_VARARGS_FOREACH_ODD_00) \
(FN, __VA_ARGS__)
#define SD_BUS_ARGS(...) __VA_ARGS__
diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c
index 205142e..6dff709 100644
--- a/src/test/test-btrfs.c
+++ b/src/test/test-btrfs.c
@@ -71,7 +71,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
if (r >= 0)
- assert_se(xopenat_lock(AT_FDCWD, "/xxxtest4", 0, 0, 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
+ assert_se(xopenat_lock(AT_FDCWD, "/xxxtest4", 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
safe_close(r);
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index f3144f0..9674e78 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -520,13 +520,35 @@ TEST(copy_lock) {
assert_se((fd = copy_directory_at(tfd, "abc", tfd, "qed", COPY_LOCK_BSD)) >= 0);
assert_se(faccessat(tfd, "qed", F_OK, 0) >= 0);
assert_se(faccessat(tfd, "qed/def", F_OK, 0) >= 0);
- assert_se(xopenat_lock(tfd, "qed", 0, 0, 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
+ assert_se(xopenat_lock(tfd, "qed", 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
fd = safe_close(fd);
assert_se((fd = copy_file_at(tfd, "abc/def", tfd, "poi", 0, 0644, COPY_LOCK_BSD)));
assert_se(read_file_at_and_streq(tfd, "poi", "abc\n"));
- assert_se(xopenat_lock(tfd, "poi", 0, 0, 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
+ assert_se(xopenat_lock(tfd, "poi", 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
fd = safe_close(fd);
}
+TEST(copy_verify_linked) {
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ _cleanup_close_ int tfd = -EBADF, fd_1 = -EBADF, fd_2 = -EBADF;
+
+ tfd = mkdtemp_open(NULL, O_PATH, &t);
+ assert_se(tfd >= 0);
+
+ assert_se(write_string_file_at(tfd, "hoge", "bar bar", WRITE_STRING_FILE_CREATE) >= 0);
+
+ fd_1 = openat(tfd, "hoge", O_CLOEXEC | O_NOCTTY | O_RDONLY);
+ assert_se(fd_1 >= 0);
+ fd_2 = openat(tfd, "hoge", O_CLOEXEC | O_NOCTTY | O_RDONLY);
+ assert_se(fd_2 >= 0);
+ assert_se(unlinkat(tfd, "hoge", 0) >= 0);
+
+ assert_se(copy_file_at(fd_1, NULL, tfd, "to_1", 0, 0644, 0) >= 0);
+ assert_se(read_file_at_and_streq(tfd, "to_1", "bar bar\n"));
+
+ assert_se(copy_file_at(fd_2, NULL, tfd, "to_2", O_EXCL, 0644, COPY_VERIFY_LINKED) == -EIDRM);
+ assert_se(faccessat(tfd, "to_2", F_OK, AT_SYMLINK_NOFOLLOW) < 0 && errno == ENOENT);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index ef335b4..b32feff 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -673,37 +673,37 @@ TEST(openat_report_new) {
assert_se(b);
}
-TEST(xopenat) {
+TEST(xopenat_full) {
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
_cleanup_close_ int tfd = -EBADF, fd = -EBADF, fd2 = -EBADF;
assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0);
- /* Test that xopenat() creates directories if O_DIRECTORY is specified. */
+ /* Test that xopenat_full() creates directories if O_DIRECTORY is specified. */
- assert_se((fd = xopenat(tfd, "abc", O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, 0, 0755)) >= 0);
+ assert_se((fd = xopenat_full(tfd, "abc", O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, 0, 0755)) >= 0);
assert_se((fd_verify_directory(fd) >= 0));
fd = safe_close(fd);
- assert_se(xopenat(tfd, "abc", O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, 0, 0755) == -EEXIST);
+ assert_se(xopenat_full(tfd, "abc", O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, 0, 0755) == -EEXIST);
- assert_se((fd = xopenat(tfd, "abc", O_DIRECTORY|O_CREAT|O_CLOEXEC, 0, 0755)) >= 0);
+ assert_se((fd = xopenat_full(tfd, "abc", O_DIRECTORY|O_CREAT|O_CLOEXEC, 0, 0755)) >= 0);
assert_se((fd_verify_directory(fd) >= 0));
fd = safe_close(fd);
- /* Test that xopenat() creates regular files if O_DIRECTORY is not specified. */
+ /* Test that xopenat_full() creates regular files if O_DIRECTORY is not specified. */
- assert_se((fd = xopenat(tfd, "def", O_CREAT|O_EXCL|O_CLOEXEC, 0, 0644)) >= 0);
+ assert_se((fd = xopenat_full(tfd, "def", O_CREAT|O_EXCL|O_CLOEXEC, 0, 0644)) >= 0);
assert_se(fd_verify_regular(fd) >= 0);
fd = safe_close(fd);
- /* Test that we can reopen an existing fd with xopenat() by specifying an empty path. */
+ /* Test that we can reopen an existing fd with xopenat_full() by specifying an empty path. */
- assert_se((fd = xopenat(tfd, "def", O_PATH|O_CLOEXEC, 0, 0)) >= 0);
- assert_se((fd2 = xopenat(fd, "", O_RDWR|O_CLOEXEC, 0, 0644)) >= 0);
+ assert_se((fd = xopenat_full(tfd, "def", O_PATH|O_CLOEXEC, 0, 0)) >= 0);
+ assert_se((fd2 = xopenat_full(fd, "", O_RDWR|O_CLOEXEC, 0, 0644)) >= 0);
}
-TEST(xopenat_lock) {
+TEST(xopenat_lock_full) {
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
_cleanup_close_ int tfd = -EBADF, fd = -EBADF;
siginfo_t si;
@@ -714,11 +714,11 @@ TEST(xopenat_lock) {
* and close the file descriptor and still properly create the directory and acquire the lock in
* another process. */
- fd = xopenat_lock(tfd, "abc", O_CREAT|O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX);
+ fd = xopenat_lock_full(tfd, "abc", O_CREAT|O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX);
assert_se(fd >= 0);
assert_se(faccessat(tfd, "abc", F_OK, 0) >= 0);
assert_se(fd_verify_directory(fd) >= 0);
- assert_se(xopenat_lock(tfd, "abc", O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
+ assert_se(xopenat_lock_full(tfd, "abc", O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
pid_t pid = fork();
assert_se(pid >= 0);
@@ -726,21 +726,21 @@ TEST(xopenat_lock) {
if (pid == 0) {
safe_close(fd);
- fd = xopenat_lock(tfd, "abc", O_CREAT|O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX);
+ fd = xopenat_lock_full(tfd, "abc", O_CREAT|O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX);
assert_se(fd >= 0);
assert_se(faccessat(tfd, "abc", F_OK, 0) >= 0);
assert_se(fd_verify_directory(fd) >= 0);
- assert_se(xopenat_lock(tfd, "abc", O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
+ assert_se(xopenat_lock_full(tfd, "abc", O_DIRECTORY|O_CLOEXEC, 0, 0755, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
_exit(EXIT_SUCCESS);
}
- /* We need to give the child process some time to get past the xopenat() call in xopenat_lock() and
- * block in the call to lock_generic() waiting for the lock to become free. We can't modify
- * xopenat_lock() to signal an eventfd to let us know when that has happened, so we just sleep for a
- * little and assume that's enough time for the child process to get along far enough. It doesn't
- * matter if it doesn't get far enough, in that case we just won't trigger the fallback logic in
- * xopenat_lock(), but the test will still succeed. */
+ /* We need to give the child process some time to get past the xopenat() call in xopenat_lock_full()
+ * and block in the call to lock_generic() waiting for the lock to become free. We can't modify
+ * xopenat_lock_full() to signal an eventfd to let us know when that has happened, so we just sleep
+ * for a little and assume that's enough time for the child process to get along far enough. It
+ * doesn't matter if it doesn't get far enough, in that case we just won't trigger the fallback logic
+ * in xopenat_lock_full(), but the test will still succeed. */
assert_se(usleep_safe(20 * USEC_PER_MSEC) >= 0);
assert_se(unlinkat(tfd, "abc", AT_REMOVEDIR) >= 0);
@@ -749,8 +749,8 @@ TEST(xopenat_lock) {
assert_se(wait_for_terminate(pid, &si) >= 0);
assert_se(si.si_code == CLD_EXITED);
- assert_se(xopenat_lock(tfd, "abc", 0, 0, 0755, LOCK_POSIX, LOCK_EX) == -EBADF);
- assert_se(xopenat_lock(tfd, "def", O_DIRECTORY, 0, 0755, LOCK_POSIX, LOCK_EX) == -EBADF);
+ assert_se(xopenat_lock_full(tfd, "abc", 0, 0, 0755, LOCK_POSIX, LOCK_EX) == -EBADF);
+ assert_se(xopenat_lock_full(tfd, "def", O_DIRECTORY, 0, 0755, LOCK_POSIX, LOCK_EX) == -EBADF);
}
static int intro(void) {
diff --git a/src/test/test-open-file.c b/src/test/test-open-file.c
index 1b938ec..4314d0d 100644
--- a/src/test/test-open-file.c
+++ b/src/test/test-open-file.c
@@ -172,14 +172,12 @@ TEST(open_file_to_string) {
assert_se(streq(s, "/proc/1/ns/mnt::read-only"));
s = mfree(s);
- assert_se(free_and_strdup(&of->path, "/path:with:colon"));
- assert_se(free_and_strdup(&of->fdname, "path:with:colon"));
+ assert_se(free_and_strdup(&of->path, "/path:with:colon") >= 0);
+ assert_se(free_and_strdup(&of->fdname, "path:with:colon") >= 0);
of->flags = 0;
- r = open_file_to_string(of, &s);
-
- assert_se(r >= 0);
- assert_se(streq(s, "/path\\:with\\:colon"));
+ assert_se(open_file_to_string(of, &s) >= 0);
+ assert_se(streq(s, "/path\\x3awith\\x3acolon"));
}
DEFINE_TEST_MAIN(LOG_INFO);
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index 5aca207..8d7fd5b 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -180,6 +180,25 @@ TEST(dir_is_empty) {
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) > 0);
}
+TEST(fd_verify_linked) {
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ _cleanup_close_ int tfd = -EBADF, fd = -EBADF;
+ _cleanup_free_ char *p = NULL;
+
+ tfd = mkdtemp_open(NULL, O_PATH, &t);
+ assert_se(tfd >= 0);
+
+ assert_se(p = path_join(t, "hoge"));
+ assert_se(touch(p) >= 0);
+
+ fd = open(p, O_CLOEXEC | O_PATH);
+ assert_se(fd >= 0);
+
+ assert_se(fd_verify_linked(fd) >= 0);
+ assert_se(unlinkat(tfd, "hoge", 0) >= 0);
+ assert_se(fd_verify_linked(fd) == -EIDRM);
+}
+
static int intro(void) {
log_show_color(true);
return EXIT_SUCCESS;
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index bc83aab..4919cb7 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -817,7 +817,7 @@ static int dir_cleanup(
cutoff_nsec, sub_path, age_by_file, false))
continue;
- fd = xopenat(dirfd(d),
+ fd = xopenat_full(dirfd(d),
de->d_name,
O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME|O_NONBLOCK,
/* xopen_flags = */ 0,
@@ -1173,60 +1173,47 @@ static int parse_acls_from_arg(Item *item) {
#if HAVE_ACL
static int parse_acl_cond_exec(
const char *path,
- acl_t access, /* could be empty (NULL) */
- acl_t cond_exec,
const struct stat *st,
+ acl_t cond_exec,
+ acl_t access, /* could be empty (NULL) */
bool append,
acl_t *ret) {
- _cleanup_(acl_freep) acl_t parsed = NULL;
acl_entry_t entry;
acl_permset_t permset;
bool has_exec;
int r;
assert(path);
- assert(ret);
assert(st);
+ assert(cond_exec);
+ assert(ret);
- parsed = access ? acl_dup(access) : acl_init(0);
- if (!parsed)
- return -errno;
-
- /* Since we substitute 'X' with 'x' in parse_acl(), we just need to copy the entries over
- * for directories */
- if (S_ISDIR(st->st_mode)) {
- for (r = acl_get_entry(cond_exec, ACL_FIRST_ENTRY, &entry);
- r > 0;
- r = acl_get_entry(cond_exec, ACL_NEXT_ENTRY, &entry)) {
-
- acl_entry_t parsed_entry;
-
- if (acl_create_entry(&parsed, &parsed_entry) < 0)
- return -errno;
-
- if (acl_copy_entry(parsed_entry, entry) < 0)
- return -errno;
- }
- if (r < 0)
- return -errno;
-
- goto finish;
- }
-
- has_exec = st->st_mode & S_IXUSR;
-
- if (!has_exec && append) {
+ if (!S_ISDIR(st->st_mode)) {
_cleanup_(acl_freep) acl_t old = NULL;
old = acl_get_file(path, ACL_TYPE_ACCESS);
if (!old)
return -errno;
+ has_exec = false;
+
for (r = acl_get_entry(old, ACL_FIRST_ENTRY, &entry);
r > 0;
r = acl_get_entry(old, ACL_NEXT_ENTRY, &entry)) {
+ acl_tag_t tag;
+
+ if (acl_get_tag_type(entry, &tag) < 0)
+ return -errno;
+
+ if (tag == ACL_MASK)
+ continue;
+
+ /* If not appending, skip ACL definitions */
+ if (!append && IN_SET(tag, ACL_USER, ACL_GROUP))
+ continue;
+
if (acl_get_permset(entry, &permset) < 0)
return -errno;
@@ -1240,28 +1227,33 @@ static int parse_acl_cond_exec(
}
if (r < 0)
return -errno;
- }
- /* Check if we're about to set the execute bit in acl_access */
- if (!has_exec && access) {
- for (r = acl_get_entry(access, ACL_FIRST_ENTRY, &entry);
- r > 0;
- r = acl_get_entry(access, ACL_NEXT_ENTRY, &entry)) {
+ /* Check if we're about to set the execute bit in acl_access */
+ if (!has_exec && access) {
+ for (r = acl_get_entry(access, ACL_FIRST_ENTRY, &entry);
+ r > 0;
+ r = acl_get_entry(access, ACL_NEXT_ENTRY, &entry)) {
- if (acl_get_permset(entry, &permset) < 0)
- return -errno;
+ if (acl_get_permset(entry, &permset) < 0)
+ return -errno;
- r = acl_get_perm(permset, ACL_EXECUTE);
+ r = acl_get_perm(permset, ACL_EXECUTE);
+ if (r < 0)
+ return -errno;
+ if (r > 0) {
+ has_exec = true;
+ break;
+ }
+ }
if (r < 0)
return -errno;
- if (r > 0) {
- has_exec = true;
- break;
- }
}
- if (r < 0)
- return -errno;
- }
+ } else
+ has_exec = true;
+
+ _cleanup_(acl_freep) acl_t parsed = access ? acl_dup(access) : acl_init(0);
+ if (!parsed)
+ return -errno;
for (r = acl_get_entry(cond_exec, ACL_FIRST_ENTRY, &entry);
r > 0;
@@ -1275,6 +1267,7 @@ static int parse_acl_cond_exec(
if (acl_copy_entry(parsed_entry, entry) < 0)
return -errno;
+ /* We substituted 'X' with 'x' in parse_acl(), so drop execute bit here if not applicable. */
if (!has_exec) {
if (acl_get_permset(parsed_entry, &permset) < 0)
return -errno;
@@ -1286,7 +1279,6 @@ static int parse_acl_cond_exec(
if (r < 0)
return -errno;
-finish:
if (!append) { /* want_mask = true */
r = calc_acl_mask_if_needed(&parsed);
if (r < 0)
@@ -1390,10 +1382,9 @@ static int fd_set_acls(
}
if (item->acl_access_exec) {
- r = parse_acl_cond_exec(FORMAT_PROC_FD_PATH(fd),
- item->acl_access,
+ r = parse_acl_cond_exec(FORMAT_PROC_FD_PATH(fd), st,
item->acl_access_exec,
- st,
+ item->acl_access,
item->append_or_force,
&access_with_exec_parsed);
if (r < 0)
diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c
index 0be7ffc..35628fc 100644
--- a/src/tpm2-setup/tpm2-setup.c
+++ b/src/tpm2-setup/tpm2-setup.c
@@ -18,6 +18,7 @@
static char *arg_tpm2_device = NULL;
static bool arg_early = false;
+static bool arg_graceful = false;
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
@@ -43,6 +44,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --tpm2-device=PATH\n"
" Pick TPM2 device\n"
" --early=BOOL Store SRK public key in /run/ rather than /var/lib/\n"
+ " --graceful Exit gracefully if no TPM2 device is found\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -59,6 +61,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100,
ARG_TPM2_DEVICE,
ARG_EARLY,
+ ARG_GRACEFUL,
};
static const struct option options[] = {
@@ -66,6 +69,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "early", required_argument, NULL, ARG_EARLY },
+ { "graceful", no_argument, NULL, ARG_GRACEFUL },
{}
};
@@ -100,6 +104,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_early = r;
break;
+ case ARG_GRACEFUL:
+ arg_graceful = true;
+ break;
+
case '?':
return -EINVAL;
@@ -204,9 +212,9 @@ static int load_public_key_tpm2(struct public_key_data *ret) {
assert(ret);
- r = tpm2_context_new(arg_tpm2_device, &c);
+ r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
r = tpm2_get_or_create_srk(
c,
@@ -247,6 +255,11 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
return r;
+ if (arg_graceful && tpm2_support() != TPM2_SUPPORT_FULL) {
+ log_notice("No complete TPM2 support detected, exiting gracefully.");
+ return EXIT_SUCCESS;
+ }
+
umask(0022);
_cleanup_(public_key_data_done) struct public_key_data runtime_key = {}, persistent_key = {}, tpm2_key = {};
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index 6abf1b6..b0d0961 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -40,6 +40,7 @@ import subprocess
import sys
import tempfile
import textwrap
+import struct
from hashlib import sha256
from typing import (Any,
Callable,
@@ -128,6 +129,45 @@ def try_import(modname, name=None):
except ImportError as e:
raise ValueError(f'Kernel is compressed with {name or modname}, but module unavailable') from e
+def get_zboot_kernel(f):
+ """Decompress zboot efistub kernel if compressed. Return contents."""
+ # See linux/drivers/firmware/efi/libstub/Makefile.zboot
+ # and linux/drivers/firmware/efi/libstub/zboot-header.S
+
+ # 4 bytes at offset 0x08 contain the starting offset of compressed data
+ f.seek(8)
+ _start = f.read(4)
+ start = struct.unpack('<i', _start)[0]
+
+ # Reading 4 bytes from address 0x0c is the size of compressed data,
+ # but it needs to be corrected according to the compressed type.
+ f.seek(0xc)
+ _sizes = f.read(4)
+ size = struct.unpack('<i', _sizes)[0]
+
+ # Read 6 bytes from address 0x18, which is a nul-terminated
+ # string representing the compressed type.
+ f.seek(0x18)
+ comp_type = f.read(6)
+ f.seek(start)
+ if comp_type.startswith(b'gzip'):
+ gzip = try_import('gzip')
+ return gzip.open(f).read(size)
+ elif comp_type.startswith(b'lz4'):
+ lz4 = try_import('lz4.frame', 'lz4')
+ return lz4.frame.decompress(f.read(size))
+ elif comp_type.startswith(b'lzma'):
+ lzma = try_import('lzma')
+ return lzma.open(f).read(size)
+ elif comp_type.startswith(b'lzo'):
+ raise NotImplementedError('lzo decompression not implemented')
+ elif comp_type.startswith(b'xzkern'):
+ raise NotImplementedError('xzkern decompression not implemented')
+ elif comp_type.startswith(b'zstd22'):
+ zstd = try_import('zstd')
+ return zstd.uncompress(f.read(size))
+ else:
+ raise NotImplementedError(f'unknown compressed type: {comp_type}')
def maybe_decompress(filename):
"""Decompress file if compressed. Return contents."""
@@ -140,8 +180,14 @@ def maybe_decompress(filename):
return f.read()
if start.startswith(b'MZ'):
- # not compressed aarch64 and riscv64
- return f.read()
+ f.seek(4)
+ img_type = f.read(4)
+ if img_type.startswith(b'zimg'):
+ # zboot efistub kernel
+ return get_zboot_kernel(f)
+ else:
+ # not compressed aarch64 and riscv64
+ return f.read()
if start.startswith(b'\x1f\x8b'):
gzip = try_import('gzip')
@@ -804,14 +850,19 @@ def make_uki(opts):
if linux is not None:
# Merge the .sbat sections from stub, kernel and parameter, so that revocation can be done on either.
- uki.add_section(Section.create('.sbat', merge_sbat([opts.stub, linux], opts.sbat), measure=True))
+ input_pes = [opts.stub, linux]
+ if not opts.sbat:
+ opts.sbat = ["""sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
+uki,1,UKI,uki,1,https://uapi-group.org/specifications/specs/unified_kernel_image/
+"""]
else:
# Addons don't use the stub so we add SBAT manually
+ input_pes = []
if not opts.sbat:
opts.sbat = ["""sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
-uki,1,UKI,uki,1,https://www.freedesktop.org/software/systemd/man/systemd-stub.html
+uki-addon,1,UKI Addon,addon,1,https://www.freedesktop.org/software/systemd/man/latest/systemd-stub.html
"""]
- uki.add_section(Section.create('.sbat', merge_sbat([], opts.sbat), measure=False))
+ uki.add_section(Section.create('.sbat', merge_sbat(input_pes, opts.sbat), measure=linux is not None))
# PCR measurement and signing
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
index a7db3fb..238a71d 100644
--- a/src/userdb/userdbctl.c
+++ b/src/userdb/userdbctl.c
@@ -310,6 +310,9 @@ static int table_add_uid_map(
assert(table);
assert(add_unavailable);
+ if (!p)
+ return 0;
+
for (size_t i = 0; p && i < p->n_entries; i++) {
UidRangeEntry *x = p->entries + i;
@@ -534,7 +537,8 @@ static int table_add_gid_boundaries(Table *table, const UidRange *p) {
for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
_cleanup_free_ char *name = NULL, *comment = NULL;
- if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last))
+ if (!uid_range_covers(p, uid_range_table[i].first,
+ uid_range_table[i].last - uid_range_table[i].first + 1))
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
diff --git a/src/userdb/userdbd-manager.c b/src/userdb/userdbd-manager.c
index c1dfe47..359c827 100644
--- a/src/userdb/userdbd-manager.c
+++ b/src/userdb/userdbd-manager.c
@@ -5,6 +5,7 @@
#include "sd-daemon.h"
#include "common-signal.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "mkdir.h"
@@ -156,7 +157,6 @@ static int start_one_worker(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to fork new worker child: %m");
if (r == 0) {
- char pids[DECIMAL_STR_MAX(pid_t)];
/* Child */
if (m->listen_fd == 3) {
@@ -174,9 +174,9 @@ static int start_one_worker(Manager *m) {
safe_close(m->listen_fd);
}
- xsprintf(pids, PID_FMT, pid);
- if (setenv("LISTEN_PID", pids, 1) < 0) {
- log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
+ r = setenvf("LISTEN_PID", /* overwrite= */ true, PID_FMT, pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set $LISTEN_PID: %m");
_exit(EXIT_FAILURE);
}