diff options
Diffstat (limited to 'src/basic')
180 files changed, 6886 insertions, 2188 deletions
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index 136d2b3..c215c33 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -20,7 +20,7 @@ typedef void* (*mfree_func_t)(void *p); * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */ #define ALLOCA_MAX (4U*1024U*1024U) -#define new(t, n) ((t*) malloc_multiply((n), sizeof(t))) +#define new(t, n) ((t*) malloc_multiply(n, sizeof(t))) #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t))) @@ -45,9 +45,9 @@ typedef void* (*mfree_func_t)(void *p); (t*) alloca0((sizeof(t)*_n_)); \ }) -#define newdup(t, p, n) ((t*) memdup_multiply(p, (n), sizeof(t))) +#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t))) -#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, (n), sizeof(t))) +#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t))) #define malloc0(n) (calloc(1, (n) ?: 1)) @@ -237,7 +237,7 @@ static inline size_t malloc_sizeof_safe(void **xp) { #define strndupa_safe(s, n) \ ({ \ const char *_t = (s); \ - (char*) memdupa_suffix0(_t, strnlen(_t, (n))); \ + (char*) memdupa_suffix0(_t, strnlen(_t, n)); \ }) /* Free every element of the array. */ diff --git a/src/basic/bitfield.h b/src/basic/bitfield.h index 25bc0eb..048e08d 100644 --- a/src/basic/bitfield.h +++ b/src/basic/bitfield.h @@ -27,7 +27,7 @@ ({ \ typeof(type) UNIQ_T(_mask, uniq) = (type)0; \ int UNIQ_T(_i, uniq); \ - VA_ARGS_FOREACH(UNIQ_T(_i, uniq), ##__VA_ARGS__) \ + FOREACH_ARGUMENT(UNIQ_T(_i, uniq), ##__VA_ARGS__) \ UNIQ_T(_mask, uniq) |= INDEX_TO_MASK(type, UNIQ_T(_i, uniq)); \ UNIQ_T(_mask, uniq); \ }) diff --git a/src/basic/build-path.c b/src/basic/build-path.c new file mode 100644 index 0000000..b597265 --- /dev/null +++ b/src/basic/build-path.c @@ -0,0 +1,274 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <elf.h> +#include <link.h> +#include <sys/auxv.h> + +#include "build-path.h" +#include "errno-list.h" +#include "errno-util.h" +#include "macro.h" +#include "path-util.h" +#include "process-util.h" +#include "unistd.h" + +static int get_runpath_from_dynamic(const ElfW(Dyn) *d, ElfW(Addr) bias, const char **ret) { + size_t runpath_index = SIZE_MAX, rpath_index = SIZE_MAX; + const char *strtab = NULL; + + assert(d); + + /* Iterates through the PT_DYNAMIC section to find the DT_RUNPATH/DT_RPATH entries */ + + for (; d->d_tag != DT_NULL; d++) { + + switch (d->d_tag) { + + case DT_RUNPATH: + runpath_index = (size_t) d->d_un.d_val; + break; + + case DT_RPATH: + rpath_index = (size_t) d->d_un.d_val; + break; + + case DT_STRTAB: + /* On MIPS and RISC-V DT_STRTAB records an offset, not a valid address, so it has to be adjusted + * using the bias calculated earlier. */ + if (d->d_un.d_val != 0) + strtab = (const char *) ((uintptr_t) d->d_un.d_val +#if defined(__mips__) || defined(__riscv) + + bias +#endif + ); + break; + } + + /* runpath wins, hence if we have the table and runpath we can exit the loop early */ + if (strtab && runpath_index != SIZE_MAX) + break; + } + + if (!strtab) + return -ENOTRECOVERABLE; + + /* According to ld.so runpath wins if both runpath and rpath are defined. */ + if (runpath_index != SIZE_MAX) { + if (ret) + *ret = strtab + runpath_index; + return 1; + } + + if (rpath_index != SIZE_MAX) { + if (ret) + *ret = strtab + rpath_index; + return 1; + } + + if (ret) + *ret = NULL; + + return 0; +} + +static int get_runpath(const char **ret) { + unsigned long phdr, phent, phnum; + + /* Finds the rpath/runpath in the program headers of the main executable we are running in */ + + phdr = getauxval(AT_PHDR); /* Start offset of phdr */ + if (phdr == 0) + return -ENOTRECOVERABLE; + + phnum = getauxval(AT_PHNUM); /* Number of entries in phdr */ + if (phnum == 0) + return -ENOTRECOVERABLE; + + phent = getauxval(AT_PHENT); /* Size of entries in phdr */ + if (phent < sizeof(ElfW(Phdr))) /* Safety check, that our idea of the structure matches the file */ + return -ENOTRECOVERABLE; + + ElfW(Addr) bias = 0, dyn = 0; + bool found_bias = false, found_dyn = false; + + /* Iterate through the Phdr structures to find the PT_PHDR and PT_DYNAMIC sections */ + for (unsigned long i = 0; i < phnum; i++) { + const ElfW(Phdr) *p = (const ElfW(Phdr)*) (phdr + (i * phent)); + + switch (p->p_type) { + + case PT_PHDR: + if (p->p_vaddr > phdr) /* safety overflow check */ + return -ENOTRECOVERABLE; + + bias = (ElfW(Addr)) phdr - p->p_vaddr; + found_bias = true; + break; + + case PT_DYNAMIC: + dyn = p->p_vaddr; + found_dyn = true; + break; + } + + if (found_bias && found_dyn) + break; + } + + if (!found_dyn) + return -ENOTRECOVERABLE; + + return get_runpath_from_dynamic((const ElfW(Dyn)*) (bias + dyn), bias, ret); +} + +int get_build_exec_dir(char **ret) { + int r; + + /* Returns the build execution directory if we are invoked in a build environment. Specifically, this + * checks if the main program binary has an rpath/runpath set (i.e. an explicit directory where to + * look for shared libraries) to $ORIGIN. If so we know that this is not a regular installed binary, + * but one which shall acquire its libraries from below a directory it is located in, i.e. a build + * directory or similar. In that case it typically makes sense to also search for our auxiliary + * executables we fork() off in a directory close to our main program binary, rather than in the + * system. + * + * This function is supposed to be used when looking for "callout" binaries that are closely related + * to the main program (i.e. speak a specific protocol between each other). And where it's generally + * a good idea to use the binary from the build tree (if there is one) instead of the system. + * + * Note that this does *not* actually return the rpath/runpath but the instead the directory the main + * executable was found in. This follows the logic that the result is supposed to be used for + * executable binaries (i.e. stuff in bindir), not for shared libraries (i.e. stuff in libdir), and + * hence the literal shared library path would just be wrong. + * + * TLDR: if we look for callouts in this dir first, running binaries from the meson build tree + * automatically uses the right callout. + * + * Returns: + * -ENOEXEC → We are not running in an rpath/runpath $ORIGIN environment + * -ENOENT → We don't know our own binary path + * -NOTRECOVERABLE → Dynamic binary information missing? + */ + + static int runpath_cached = -ERRNO_MAX-1; + if (runpath_cached == -ERRNO_MAX-1) { + const char *runpath = NULL; + + runpath_cached = get_runpath(&runpath); + + /* We only care if the runpath starts with $ORIGIN/ */ + if (runpath_cached > 0 && !startswith(runpath, "$ORIGIN/")) + runpath_cached = 0; + } + if (runpath_cached < 0) + return runpath_cached; + if (runpath_cached == 0) + return -ENOEXEC; + + _cleanup_free_ char *exe = NULL; + r = get_process_exe(0, &exe); + if (r < 0) + return runpath_cached = r; + + return path_extract_directory(exe, ret); +} + +static int find_build_dir_binary(const char *fn, char **ret) { + int r; + + assert(fn); + assert(ret); + + _cleanup_free_ char *build_dir = NULL; + r = get_build_exec_dir(&build_dir); + if (r < 0) + return r; + + _cleanup_free_ char *np = path_join(build_dir, fn); + if (!np) + return -ENOMEM; + + *ret = TAKE_PTR(np); + return 0; +} + +static int find_environment_binary(const char *fn, const char **ret) { + + /* If a path such as /usr/lib/systemd/systemd-foobar is specified, then this will check for an + * environment variable SYSTEMD_FOOBAR_PATH and return it if set. */ + + _cleanup_free_ char *s = strdup(fn); + if (!s) + return -ENOMEM; + + ascii_strupper(s); + string_replace_char(s, '-', '_'); + + if (!strextend(&s, "_PATH")) + return -ENOMEM; + + const char *e; + e = secure_getenv(s); + if (!e) + return -ENXIO; + + *ret = e; + return 0; +} + +int invoke_callout_binary(const char *path, char *const argv[]) { + int r; + + assert(path); + + /* Just like execv(), but tries to execute the specified binary in the build dir instead, if known */ + + _cleanup_free_ char *fn = NULL; + r = path_extract_filename(path, &fn); + if (r < 0) + return r; + if (r == O_DIRECTORY) /* Uh? */ + return -EISDIR; + + const char *e; + if (find_environment_binary(fn, &e) >= 0) { + /* If there's an explicit environment variable set for this binary, prefer it */ + execv(e, argv); + return -errno; /* The environment variable counts, let's fail otherwise */ + } + + _cleanup_free_ char *np = NULL; + if (find_build_dir_binary(fn, &np) >= 0) + execv(np, argv); + + execv(path, argv); + return -errno; +} + +int pin_callout_binary(const char *path) { + int r; + + assert(path); + + /* Similar to invoke_callout_binary(), but pins (i.e. O_PATH opens) the binary instead of executing it. */ + + _cleanup_free_ char *fn = NULL; + r = path_extract_filename(path, &fn); + if (r < 0) + return r; + if (r == O_DIRECTORY) /* Uh? */ + return -EISDIR; + + const char *e; + if (find_environment_binary(fn, &e) >= 0) + return RET_NERRNO(open(e, O_CLOEXEC|O_PATH)); + + _cleanup_free_ char *np = NULL; + if (find_build_dir_binary(fn, &np) >= 0) { + r = RET_NERRNO(open(np, O_CLOEXEC|O_PATH)); + if (r >= 0) + return r; + } + + return RET_NERRNO(open(path, O_CLOEXEC|O_PATH)); +} diff --git a/src/basic/build-path.h b/src/basic/build-path.h new file mode 100644 index 0000000..6c38a4a --- /dev/null +++ b/src/basic/build-path.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int get_build_exec_dir(char **ret); + +int invoke_callout_binary(const char *path, char *const argv[]); + +int pin_callout_binary(const char *path); diff --git a/src/basic/build.c b/src/basic/build.c index c587ada..3ab25f7 100644 --- a/src/basic/build.c +++ b/src/basic/build.c @@ -138,6 +138,12 @@ const char* const systemd_features = " -LIBCRYPTSETUP" #endif +#if HAVE_LIBCRYPTSETUP_PLUGINS + " +LIBCRYPTSETUP_PLUGINS" +#else + " -LIBCRYPTSETUP_PLUGINS" +#endif + #if HAVE_LIBFDISK " +LIBFDISK" #else @@ -232,7 +238,12 @@ const char* const systemd_features = " -SYSVINIT" #endif - " default-hierarchy=" DEFAULT_HIERARCHY_NAME +#if HAVE_LIBARCHIVE + " +LIBARCHIVE" +#else + " -LIBARCHIVE" +#endif + ; static char *systemd_features_with_color(void) { @@ -276,7 +287,7 @@ int version(void) { if (colors_enabled()) b = systemd_features_with_color(); - printf("%ssystemd " STRINGIFY(PROJECT_VERSION) "%s (" GIT_VERSION ")\n%s\n", + printf("%ssystemd " PROJECT_VERSION_FULL "%s (" GIT_VERSION ")\n%s\n", ansi_highlight(), ansi_normal(), b ?: systemd_features); return 0; diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index c3cf455..e9b41fe 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include <errno.h> +#include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <sys/prctl.h> @@ -34,37 +35,38 @@ int have_effective_cap(int value) { } unsigned cap_last_cap(void) { - static thread_local unsigned saved; - static thread_local bool valid = false; - _cleanup_free_ char *content = NULL; - unsigned long p = 0; - int r; + static atomic_int saved = INT_MAX; + int r, c; - if (valid) - return saved; + c = saved; + if (c != INT_MAX) + return c; - /* available since linux-3.2 */ + /* Available since linux-3.2 */ + _cleanup_free_ char *content = NULL; r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); - if (r >= 0) { - r = safe_atolu(content, &p); - if (r >= 0) { - - if (p > CAP_LIMIT) /* Safety for the future: if one day the kernel learns more than + if (r < 0) + log_debug_errno(r, "Failed to read /proc/sys/kernel/cap_last_cap, ignoring: %m"); + else { + r = safe_atoi(content, &c); + if (r < 0) + log_debug_errno(r, "Failed to parse /proc/sys/kernel/cap_last_cap, ignoring: %m"); + else { + if (c > CAP_LIMIT) /* Safety for the future: if one day the kernel learns more than * 64 caps, then we are in trouble (since we, as much userspace * and kernel space store capability masks in uint64_t types). We * also want to use UINT64_MAX as marker for "unset". Hence let's * hence protect ourselves against that and always cap at 62 for * now. */ - p = CAP_LIMIT; + c = CAP_LIMIT; - saved = p; - valid = true; - return p; + saved = c; + return c; } } - /* fall back to syscall-probing for pre linux-3.2 */ - p = (unsigned long) MIN(CAP_LAST_CAP, CAP_LIMIT); + /* Fall back to syscall-probing for pre linux-3.2, or where /proc/ is not mounted */ + unsigned long p = (unsigned long) MIN(CAP_LAST_CAP, CAP_LIMIT); if (prctl(PR_CAPBSET_READ, p) < 0) { @@ -81,10 +83,9 @@ unsigned cap_last_cap(void) { break; } - saved = p; - valid = true; - - return p; + c = (int) p; + saved = c; + return c; } int capability_update_inherited_set(cap_t caps, uint64_t set) { diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 18b16ec..553ee60 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -22,6 +22,7 @@ #include "log.h" #include "login-util.h" #include "macro.h" +#include "missing_fs.h" #include "missing_magic.h" #include "missing_threads.h" #include "mkdir.h" @@ -39,6 +40,38 @@ #include "user-util.h" #include "xattr-util.h" +int cg_path_open(const char *controller, const char *path) { + _cleanup_free_ char *fs = NULL; + int r; + + r = cg_get_path(controller, path, /* item=*/ NULL, &fs); + if (r < 0) + return r; + + return RET_NERRNO(open(fs, O_DIRECTORY|O_CLOEXEC)); +} + +int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) { + _cleanup_close_ int fsfd = -EBADF; + + if (cgroupfs_fd < 0) { + fsfd = open("/sys/fs/cgroup", O_CLOEXEC|O_DIRECTORY); + if (fsfd < 0) + return -errno; + + cgroupfs_fd = fsfd; + } + + cg_file_handle fh = CG_FILE_HANDLE_INIT; + CG_FILE_HANDLE_CGROUPID(fh) = id; + + int fd = open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC); + if (fd < 0) + return -errno; + + return fd; +} + static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) { _cleanup_free_ char *fs = NULL; FILE *f; @@ -62,7 +95,7 @@ int cg_enumerate_processes(const char *controller, const char *path, FILE **ret) return cg_enumerate_items(controller, path, ret, "cgroup.procs"); } -int cg_read_pid(FILE *f, pid_t *ret) { +int cg_read_pid(FILE *f, pid_t *ret, CGroupFlags flags) { unsigned long ul; /* Note that the cgroup.procs might contain duplicates! See cgroups.txt for details. */ @@ -70,27 +103,33 @@ int cg_read_pid(FILE *f, pid_t *ret) { assert(f); assert(ret); - errno = 0; - if (fscanf(f, "%lu", &ul) != 1) { + for (;;) { + errno = 0; + if (fscanf(f, "%lu", &ul) != 1) { - if (feof(f)) { - *ret = 0; - return 0; + if (feof(f)) { + *ret = 0; + return 0; + } + + return errno_or_else(EIO); } - return errno_or_else(EIO); - } + if (ul > PID_T_MAX) + return -EIO; - if (ul <= 0) - return -EIO; - if (ul > PID_T_MAX) - return -EIO; + /* In some circumstances (e.g. WSL), cgroups might contain unmappable PIDs from other + * contexts. These show up as zeros, and depending on the caller, can either be plain + * skipped over, or returned as-is. */ + if (ul == 0 && !FLAGS_SET(flags, CGROUP_DONT_SKIP_UNMAPPED)) + continue; - *ret = (pid_t) ul; - return 1; + *ret = (pid_t) ul; + return 1; + } } -int cg_read_pidref(FILE *f, PidRef *ret) { +int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags) { int r; assert(f); @@ -99,14 +138,22 @@ int cg_read_pidref(FILE *f, PidRef *ret) { for (;;) { pid_t pid; - r = cg_read_pid(f, &pid); + r = cg_read_pid(f, &pid, flags); if (r < 0) - return r; + return log_debug_errno(r, "Failed to read pid from cgroup item: %m"); if (r == 0) { *ret = PIDREF_NULL; return 0; } + if (pid == 0) + return -EREMOTE; + + if (FLAGS_SET(flags, CGROUP_NO_PIDFD)) { + *ret = PIDREF_MAKE_FROM_PID(pid); + return 1; + } + r = pidref_set_pid(ret, pid); if (r >= 0) return 1; @@ -135,7 +182,7 @@ int cg_read_event( return r; for (const char *p = content;;) { - _cleanup_free_ char *line = NULL, *key = NULL, *val = NULL; + _cleanup_free_ char *line = NULL, *key = NULL; const char *q; r = extract_first_word(&p, &line, "\n", 0); @@ -154,12 +201,7 @@ int cg_read_event( if (!streq(key, event)) continue; - val = strdup(q); - if (!val) - return -ENOMEM; - - *ret = TAKE_PTR(val); - return 0; + return strdup_to(ret, q); } } @@ -234,20 +276,13 @@ int cg_read_subgroup(DIR *d, char **ret) { assert(ret); FOREACH_DIRENT_ALL(de, d, return -errno) { - char *b; - if (de->d_type != DT_DIR) continue; if (dot_or_dot_dot(de->d_name)) continue; - b = strdup(de->d_name); - if (!b) - return -ENOMEM; - - *ret = b; - return 1; + return strdup_to_full(ret, de->d_name); } *ret = NULL; @@ -317,14 +352,14 @@ static int cg_kill_items( if (r == -ENOENT) break; if (r < 0) - return RET_GATHER(ret, r); + return RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup items: %m")); for (;;) { _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; - r = cg_read_pidref(f, &pidref); + r = cg_read_pidref(f, &pidref, flags); if (r < 0) - return RET_GATHER(ret, r); + return RET_GATHER(ret, log_debug_errno(r, "Failed to read pidref from cgroup '%s': %m", path)); if (r == 0) break; @@ -340,7 +375,7 @@ static int cg_kill_items( /* If we haven't killed this process yet, kill it */ r = pidref_kill(&pidref, sig); if (r < 0 && r != -ESRCH) - RET_GATHER(ret, r); + RET_GATHER(ret, log_debug_errno(r, "Failed to kill process with pid " PID_FMT " from cgroup '%s': %m", pidref.pid, path)); if (r >= 0) { if (flags & CGROUP_SIGCONT) (void) pidref_kill(&pidref, SIGCONT); @@ -379,6 +414,8 @@ int cg_kill( int r, ret; r = cg_kill_items(path, sig, flags, s, log_kill, userdata, "cgroup.procs"); + if (r < 0) + log_debug_errno(r, "Failed to kill processes in cgroup '%s' item cgroup.procs: %m", path); if (r < 0 || sig != SIGKILL) return r; @@ -393,9 +430,13 @@ int cg_kill( if (r == 0) return ret; - r = cg_kill_items(path, sig, flags, s, log_kill, userdata, "cgroup.threads"); + /* Opening pidfds for non thread group leaders only works from 6.9 onwards with PIDFD_THREAD. On + * older kernels or without PIDFD_THREAD pidfd_open() fails with EINVAL. Since we might read non + * thread group leader IDs from cgroup.threads, we set CGROUP_NO_PIDFD to avoid trying open pidfd's + * for them and instead use the regular pid. */ + r = cg_kill_items(path, sig, flags|CGROUP_NO_PIDFD, s, log_kill, userdata, "cgroup.threads"); if (r < 0) - return r; + return log_debug_errno(r, "Failed to kill processes in cgroup '%s' item cgroup.threads: %m", path); return r > 0 || ret > 0; } @@ -418,7 +459,7 @@ int cg_kill_kernel_sigkill(const char *path) { r = write_string_file(killfile, "1", WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return r; + return log_debug_errno(r, "Failed to write to cgroup.kill for cgroup '%s': %m", path); return 0; } @@ -455,7 +496,7 @@ int cg_kill_recursive( r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d); if (r < 0) { if (r != -ENOENT) - RET_GATHER(ret, r); + RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup '%s' subgroups: %m", path)); return ret; } @@ -465,7 +506,7 @@ int cg_kill_recursive( r = cg_read_subgroup(d, &fn); if (r < 0) { - RET_GATHER(ret, r); + RET_GATHER(ret, log_debug_errno(r, "Failed to read subgroup from cgroup '%s': %m", path)); break; } if (r == 0) @@ -476,6 +517,8 @@ int cg_kill_recursive( return -ENOMEM; r = cg_kill_recursive(p, sig, flags, s, log_kill, userdata); + if (r < 0) + log_debug_errno(r, "Failed to recursively kill processes in cgroup '%s': %m", p); if (r != 0 && ret >= 0) ret = r; } @@ -484,7 +527,7 @@ int cg_kill_recursive( if (FLAGS_SET(flags, CGROUP_REMOVE)) { r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, path); if (!IN_SET(r, -ENOENT, -EBUSY)) - RET_GATHER(ret, r); + RET_GATHER(ret, log_debug_errno(r, "Failed to remove cgroup '%s': %m", path)); } return ret; @@ -917,7 +960,7 @@ int cg_is_empty(const char *controller, const char *path) { if (r < 0) return r; - r = cg_read_pid(f, &pid); + r = cg_read_pid(f, &pid, CGROUP_DONT_SKIP_UNMAPPED); if (r < 0) return r; @@ -1125,44 +1168,29 @@ int cg_pid_get_path_shifted(pid_t pid, const char *root, char **ret_cgroup) { if (r < 0) return r; - if (c == raw) + if (c == raw) { *ret_cgroup = TAKE_PTR(raw); - else { - char *n; - - n = strdup(c); - if (!n) - return -ENOMEM; - - *ret_cgroup = n; + return 0; } - return 0; + return strdup_to(ret_cgroup, c); } int cg_path_decode_unit(const char *cgroup, char **ret_unit) { - char *c, *s; - size_t n; - assert(cgroup); assert(ret_unit); - n = strcspn(cgroup, "/"); + size_t n = strcspn(cgroup, "/"); if (n < 3) return -ENXIO; - c = strndupa_safe(cgroup, n); + char *c = strndupa_safe(cgroup, n); c = cg_unescape(c); if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) return -ENXIO; - s = strdup(c); - if (!s) - return -ENOMEM; - - *ret_unit = s; - return 0; + return strdup_to(ret_unit, c); } static bool valid_slice_name(const char *p, size_t n) { @@ -1431,7 +1459,7 @@ int cg_pid_get_machine_name(pid_t pid, char **ret_machine) { int cg_path_get_cgroupid(const char *path, uint64_t *ret) { cg_file_handle fh = CG_FILE_HANDLE_INIT; - int mnt_id = -1; + int mnt_id; assert(path); assert(ret); @@ -1445,6 +1473,20 @@ int cg_path_get_cgroupid(const char *path, uint64_t *ret) { return 0; } +int cg_fd_get_cgroupid(int fd, uint64_t *ret) { + cg_file_handle fh = CG_FILE_HANDLE_INIT; + int mnt_id = -1; + + assert(fd >= 0); + assert(ret); + + if (name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH) < 0) + return -errno; + + *ret = CG_FILE_HANDLE_CGROUPID(fh); + return 0; +} + int cg_path_get_session(const char *path, char **ret_session) { _cleanup_free_ char *unit = NULL; char *start, *end; @@ -1467,17 +1509,10 @@ int cg_path_get_session(const char *path, char **ret_session) { if (!session_id_valid(start)) return -ENXIO; - if (ret_session) { - char *rr; - - rr = strdup(start); - if (!rr) - return -ENOMEM; - - *ret_session = rr; - } + if (!ret_session) + return 0; - return 0; + return strdup_to(ret_session, start); } int cg_pid_get_session(pid_t pid, char **ret_session) { @@ -1534,34 +1569,26 @@ int cg_path_get_slice(const char *p, char **ret_slice) { assert(p); assert(ret_slice); - /* Finds the right-most slice unit from the beginning, but - * stops before we come to the first non-slice unit. */ + /* Finds the right-most slice unit from the beginning, but stops before we come to + * the first non-slice unit. */ for (;;) { - size_t n; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (!valid_slice_name(p, n)) { - - if (!e) { - char *s; + const char *s; + int n; - s = strdup(SPECIAL_ROOT_SLICE); - if (!s) - return -ENOMEM; + n = path_find_first_component(&p, /* accept_dot_dot = */ false, &s); + if (n < 0) + return n; + if (!valid_slice_name(s, n)) + break; - *ret_slice = s; - return 0; - } + e = s; + } - return cg_path_decode_unit(e, ret_slice); - } + if (e) + return cg_path_decode_unit(e, ret_slice); - e = p; - p += n; - } + return strdup_to(ret_slice, SPECIAL_ROOT_SLICE); } int cg_pid_get_slice(pid_t pid, char **ret_slice) { @@ -1714,15 +1741,8 @@ int cg_slice_to_path(const char *unit, char **ret) { assert(unit); assert(ret); - if (streq(unit, SPECIAL_ROOT_SLICE)) { - char *x; - - x = strdup(""); - if (!x) - return -ENOMEM; - *ret = x; - return 0; - } + if (streq(unit, SPECIAL_ROOT_SLICE)) + return strdup_to(ret, ""); if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN)) return -EINVAL; @@ -2141,15 +2161,14 @@ int cg_kernel_controllers(Set **ret) { _cleanup_free_ char *controller = NULL; int enabled = 0; - errno = 0; if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { + if (ferror(f)) + return -errno; + if (feof(f)) break; - if (ferror(f)) - return errno_or_else(EIO); - return -EBADMSG; } diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index d06eb6d..a887178 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -67,7 +67,7 @@ typedef enum CGroupMask { /* All real cgroup v2 controllers */ CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS, - /* All controllers we want to delegate in case of Delegate=yes. Which are prety much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */ + /* All controllers we want to delegate in case of Delegate=yes. Which are pretty much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */ CGROUP_MASK_DELEGATE = CGROUP_MASK_V2, /* All cgroup v2 BPF pseudo-controllers */ @@ -180,20 +180,25 @@ typedef enum CGroupUnified { * generate paths with multiple adjacent / removed. */ +int cg_path_open(const char *controller, const char *path); +int cg_cgroupid_open(int fsfd, uint64_t id); + +typedef enum CGroupFlags { + CGROUP_SIGCONT = 1 << 0, + CGROUP_IGNORE_SELF = 1 << 1, + CGROUP_REMOVE = 1 << 2, + CGROUP_DONT_SKIP_UNMAPPED = 1 << 3, + CGROUP_NO_PIDFD = 1 << 4, +} CGroupFlags; + int cg_enumerate_processes(const char *controller, const char *path, FILE **ret); -int cg_read_pid(FILE *f, pid_t *ret); -int cg_read_pidref(FILE *f, PidRef *ret); +int cg_read_pid(FILE *f, pid_t *ret, CGroupFlags flags); +int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags); int cg_read_event(const char *controller, const char *path, const char *event, char **ret); int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret); int cg_read_subgroup(DIR *d, char **ret); -typedef enum CGroupFlags { - CGROUP_SIGCONT = 1 << 0, - CGROUP_IGNORE_SELF = 1 << 1, - CGROUP_REMOVE = 1 << 2, -} CGroupFlags; - typedef int (*cg_kill_log_func_t)(const PidRef *pid, int sig, void *userdata); int cg_kill(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); @@ -218,7 +223,7 @@ int cg_is_delegated_fd(int fd); int cg_has_coredump_receive(const char *path); -typedef enum { +typedef enum { CG_KEY_MODE_GRACEFUL = 1 << 0, } CGroupKeyMode; @@ -267,6 +272,7 @@ int cg_is_empty_recursive(const char *controller, const char *path); int cg_get_root_path(char **path); int cg_path_get_cgroupid(const char *path, uint64_t *ret); +int cg_fd_get_cgroupid(int fd, uint64_t *ret); int cg_path_get_session(const char *path, char **ret_session); int cg_path_get_owner_uid(const char *path, uid_t *ret_uid); int cg_path_get_unit(const char *path, char **ret_unit); @@ -352,5 +358,10 @@ typedef union { uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)]; } cg_file_handle; -#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) } +#define CG_FILE_HANDLE_INIT \ + (cg_file_handle) { \ + .file_handle.handle_bytes = sizeof(uint64_t), \ + .file_handle.handle_type = FILEID_KERNFS, \ + } + #define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle) diff --git a/src/basic/chase.c b/src/basic/chase.c index 9f5477e..4576e4b 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -641,8 +641,8 @@ int chase(const char *path, const char *root, ChaseFlags flags, char **ret_path, * absolute, hence it is not necessary to prefix with the root. When "root" points to * a non-root directory, the result path is always normalized and relative, hence * we can simply call path_join() and not necessary to call path_simplify(). - * Note that the result of chaseat() may start with "." (more specifically, it may be - * "." or "./"), and we need to drop "." in that case. */ + * As a special case, chaseat() may return "." or "./", which are normalized too, + * but we need to drop "." before merging with root. */ if (empty_or_root(root)) assert(path_is_absolute(p)); @@ -651,7 +651,7 @@ int chase(const char *path, const char *root, ChaseFlags flags, char **ret_path, assert(!path_is_absolute(p)); - q = path_join(root, p + (*p == '.')); + q = path_join(root, p + STR_IN_SET(p, ".", "./")); if (!q) return -ENOMEM; @@ -741,12 +741,7 @@ int chase_extract_filename(const char *path, const char *root, char **ret) { return r; } - char *fname = strdup("."); - if (!fname) - return -ENOMEM; - - *ret = fname; - return 0; + return strdup_to(ret, "."); } int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path) { diff --git a/src/basic/compress.c b/src/basic/compress.c index ac0bfdf..33b27d3 100644 --- a/src/basic/compress.c +++ b/src/basic/compress.c @@ -12,11 +12,6 @@ #include <lzma.h> #endif -#if HAVE_LZ4 -#include <lz4.h> -#include <lz4frame.h> -#endif - #if HAVE_ZSTD #include <zstd.h> #include <zstd_errors.h> @@ -34,16 +29,52 @@ #include "unaligned.h" #if HAVE_LZ4 -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_compressionContext_t, LZ4F_freeCompressionContext, NULL); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext, NULL); +static void *lz4_dl = NULL; + +static DLSYM_FUNCTION(LZ4F_compressBegin); +static DLSYM_FUNCTION(LZ4F_compressBound); +static DLSYM_FUNCTION(LZ4F_compressEnd); +static DLSYM_FUNCTION(LZ4F_compressUpdate); +static DLSYM_FUNCTION(LZ4F_createCompressionContext); +static DLSYM_FUNCTION(LZ4F_createDecompressionContext); +static DLSYM_FUNCTION(LZ4F_decompress); +static DLSYM_FUNCTION(LZ4F_freeCompressionContext); +static DLSYM_FUNCTION(LZ4F_freeDecompressionContext); +static DLSYM_FUNCTION(LZ4F_isError); +DLSYM_FUNCTION(LZ4_compress_default); +DLSYM_FUNCTION(LZ4_decompress_safe); +DLSYM_FUNCTION(LZ4_decompress_safe_partial); +DLSYM_FUNCTION(LZ4_versionNumber); + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_compressionContext_t, sym_LZ4F_freeCompressionContext, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_decompressionContext_t, sym_LZ4F_freeDecompressionContext, NULL); #endif #if HAVE_ZSTD -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx*, ZSTD_freeCCtx, NULL); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx*, ZSTD_freeDCtx, NULL); +static void *zstd_dl = NULL; + +static DLSYM_FUNCTION(ZSTD_CCtx_setParameter); +static DLSYM_FUNCTION(ZSTD_compress); +static DLSYM_FUNCTION(ZSTD_compressStream2); +static DLSYM_FUNCTION(ZSTD_createCCtx); +static DLSYM_FUNCTION(ZSTD_createDCtx); +static DLSYM_FUNCTION(ZSTD_CStreamInSize); +static DLSYM_FUNCTION(ZSTD_CStreamOutSize); +static DLSYM_FUNCTION(ZSTD_decompressStream); +static DLSYM_FUNCTION(ZSTD_DStreamInSize); +static DLSYM_FUNCTION(ZSTD_DStreamOutSize); +static DLSYM_FUNCTION(ZSTD_freeCCtx); +static DLSYM_FUNCTION(ZSTD_freeDCtx); +static DLSYM_FUNCTION(ZSTD_getErrorCode); +static DLSYM_FUNCTION(ZSTD_getErrorName); +static DLSYM_FUNCTION(ZSTD_getFrameContentSize); +static DLSYM_FUNCTION(ZSTD_isError); + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx*, sym_ZSTD_freeCCtx, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx*, sym_ZSTD_freeDCtx, NULL); static int zstd_ret_to_errno(size_t ret) { - switch (ZSTD_getErrorCode(ret)) { + switch (sym_ZSTD_getErrorCode(ret)) { case ZSTD_error_dstSize_tooSmall: return -ENOBUFS; case ZSTD_error_memory_allocation: @@ -54,6 +85,27 @@ static int zstd_ret_to_errno(size_t ret) { } #endif +#if HAVE_XZ +static void *lzma_dl = NULL; + +static DLSYM_FUNCTION(lzma_code); +static DLSYM_FUNCTION(lzma_easy_encoder); +static DLSYM_FUNCTION(lzma_end); +static DLSYM_FUNCTION(lzma_stream_buffer_encode); +static DLSYM_FUNCTION(lzma_stream_decoder); + +/* We can't just do _cleanup_(sym_lzma_end) because a compiler bug makes + * this fail with: + * ../src/basic/compress.c: In function ‘decompress_blob_xz’: + * ../src/basic/compress.c:304:9: error: cleanup argument not a function + * 304 | _cleanup_(sym_lzma_end) lzma_stream s = LZMA_STREAM_INIT; + * | ^~~~~~~~~ + */ +static inline void lzma_end_wrapper(lzma_stream *ls) { + sym_lzma_end(ls); +} +#endif + #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t)) static const char* const compression_table[_COMPRESSION_MAX] = { @@ -75,8 +127,33 @@ bool compression_supported(Compression c) { return c >= 0 && c < _COMPRESSION_MAX && FLAGS_SET(supported, 1U << c); } +#if HAVE_XZ +int dlopen_lzma(void) { + ELF_NOTE_DLOPEN("lzma", + "Support lzma compression in journal and coredump files", + COMPRESSION_PRIORITY_XZ, + "liblzma.so.5"); + + return dlopen_many_sym_or_warn( + &lzma_dl, + "liblzma.so.5", LOG_DEBUG, + DLSYM_ARG(lzma_code), + DLSYM_ARG(lzma_easy_encoder), + DLSYM_ARG(lzma_end), + DLSYM_ARG(lzma_stream_buffer_encode), + DLSYM_ARG(lzma_stream_decoder)); +} +#endif + int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size) { + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size > 0); + assert(dst_size); + #if HAVE_XZ static const lzma_options_lzma opt = { 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT, @@ -88,12 +165,11 @@ int compress_blob_xz(const void *src, uint64_t src_size, }; lzma_ret ret; size_t out_pos = 0; + int r; - assert(src); - assert(src_size > 0); - assert(dst); - assert(dst_alloc_size > 0); - assert(dst_size); + r = dlopen_lzma(); + if (r < 0) + return r; /* Returns < 0 if we couldn't compress the data or the * compressed result is longer than the original */ @@ -101,7 +177,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, if (src_size < 80) return -ENOBUFS; - ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL, + ret = sym_lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL, src, src_size, dst, &out_pos, dst_alloc_size); if (ret != LZMA_OK) return -ENOBUFS; @@ -113,10 +189,35 @@ int compress_blob_xz(const void *src, uint64_t src_size, #endif } +#if HAVE_LZ4 +int dlopen_lz4(void) { + ELF_NOTE_DLOPEN("lz4", + "Support lz4 compression in journal and coredump files", + COMPRESSION_PRIORITY_LZ4, + "liblz4.so.1"); + + return dlopen_many_sym_or_warn( + &lz4_dl, + "liblz4.so.1", LOG_DEBUG, + DLSYM_ARG(LZ4F_compressBegin), + DLSYM_ARG(LZ4F_compressBound), + DLSYM_ARG(LZ4F_compressEnd), + DLSYM_ARG(LZ4F_compressUpdate), + DLSYM_ARG(LZ4F_createCompressionContext), + DLSYM_ARG(LZ4F_createDecompressionContext), + DLSYM_ARG(LZ4F_decompress), + DLSYM_ARG(LZ4F_freeCompressionContext), + DLSYM_ARG(LZ4F_freeDecompressionContext), + DLSYM_ARG(LZ4F_isError), + DLSYM_ARG(LZ4_compress_default), + DLSYM_ARG(LZ4_decompress_safe), + DLSYM_ARG(LZ4_decompress_safe_partial), + DLSYM_ARG(LZ4_versionNumber)); +} +#endif + int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size) { -#if HAVE_LZ4 - int r; assert(src); assert(src_size > 0); @@ -124,13 +225,19 @@ int compress_blob_lz4(const void *src, uint64_t src_size, assert(dst_alloc_size > 0); assert(dst_size); +#if HAVE_LZ4 + int r; + + r = dlopen_lz4(); + if (r < 0) + return r; /* Returns < 0 if we couldn't compress the data or the * compressed result is longer than the original */ if (src_size < 9) return -ENOBUFS; - r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); + r = sym_LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); if (r <= 0) return -ENOBUFS; @@ -143,11 +250,38 @@ int compress_blob_lz4(const void *src, uint64_t src_size, #endif } +#if HAVE_ZSTD +int dlopen_zstd(void) { + ELF_NOTE_DLOPEN("zstd", + "Support zstd compression in journal and coredump files", + COMPRESSION_PRIORITY_ZSTD, + "libzstd.so.1"); + + return dlopen_many_sym_or_warn( + &zstd_dl, + "libzstd.so.1", LOG_DEBUG, + DLSYM_ARG(ZSTD_getErrorCode), + DLSYM_ARG(ZSTD_compress), + DLSYM_ARG(ZSTD_getFrameContentSize), + DLSYM_ARG(ZSTD_decompressStream), + DLSYM_ARG(ZSTD_getErrorName), + DLSYM_ARG(ZSTD_DStreamOutSize), + DLSYM_ARG(ZSTD_CStreamInSize), + DLSYM_ARG(ZSTD_CStreamOutSize), + DLSYM_ARG(ZSTD_CCtx_setParameter), + DLSYM_ARG(ZSTD_compressStream2), + DLSYM_ARG(ZSTD_DStreamInSize), + DLSYM_ARG(ZSTD_freeCCtx), + DLSYM_ARG(ZSTD_freeDCtx), + DLSYM_ARG(ZSTD_isError), + DLSYM_ARG(ZSTD_createDCtx), + DLSYM_ARG(ZSTD_createCCtx)); +} +#endif + int compress_blob_zstd( const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size) { -#if HAVE_ZSTD - size_t k; assert(src); assert(src_size > 0); @@ -155,8 +289,16 @@ int compress_blob_zstd( assert(dst_alloc_size > 0); assert(dst_size); - k = ZSTD_compress(dst, dst_alloc_size, src, src_size, 0); - if (ZSTD_isError(k)) +#if HAVE_ZSTD + size_t k; + int r; + + r = dlopen_zstd(); + if (r < 0) + return r; + + k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, 0); + if (sym_ZSTD_isError(k)) return zstd_ret_to_errno(k); *dst_size = k; @@ -173,17 +315,22 @@ int decompress_blob_xz( size_t* dst_size, size_t dst_max) { -#if HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; - lzma_ret ret; - size_t space; - assert(src); assert(src_size > 0); assert(dst); assert(dst_size); - ret = lzma_stream_decoder(&s, UINT64_MAX, 0); +#if HAVE_XZ + _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + size_t space; + int r; + + r = dlopen_lzma(); + if (r < 0) + return r; + + ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0); if (ret != LZMA_OK) return -ENOMEM; @@ -200,7 +347,7 @@ int decompress_blob_xz( for (;;) { size_t used; - ret = lzma_code(&s, LZMA_FINISH); + ret = sym_lzma_code(&s, LZMA_FINISH); if (ret == LZMA_STREAM_END) break; @@ -235,15 +382,19 @@ int decompress_blob_lz4( size_t* dst_size, size_t dst_max) { -#if HAVE_LZ4 - char* out; - int r, size; /* LZ4 uses int for size */ - assert(src); assert(src_size > 0); assert(dst); assert(dst_size); +#if HAVE_LZ4 + char* out; + int r, size; /* LZ4 uses int for size */ + + r = dlopen_lz4(); + if (r < 0) + return r; + if (src_size <= 8) return -EBADMSG; @@ -254,7 +405,7 @@ int decompress_blob_lz4( if (!out) return -ENOMEM; - r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size); + r = sym_LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size); if (r < 0 || r != size) return -EBADMSG; @@ -272,15 +423,20 @@ int decompress_blob_zstd( size_t *dst_size, size_t dst_max) { -#if HAVE_ZSTD - uint64_t size; - assert(src); assert(src_size > 0); assert(dst); assert(dst_size); - size = ZSTD_getFrameContentSize(src, src_size); +#if HAVE_ZSTD + uint64_t size; + int r; + + r = dlopen_zstd(); + if (r < 0) + return r; + + size = sym_ZSTD_getFrameContentSize(src, src_size); if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN)) return -EBADMSG; @@ -289,10 +445,10 @@ int decompress_blob_zstd( if (size > SIZE_MAX) return -E2BIG; - if (!(greedy_realloc(dst, MAX(ZSTD_DStreamOutSize(), size), 1))) + if (!(greedy_realloc(dst, MAX(sym_ZSTD_DStreamOutSize(), size), 1))) return -ENOMEM; - _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx(); + _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx(); if (!dctx) return -ENOMEM; @@ -305,9 +461,9 @@ int decompress_blob_zstd( .size = MALLOC_SIZEOF_SAFE(*dst), }; - size_t k = ZSTD_decompressStream(dctx, &output, &input); - if (ZSTD_isError(k)) { - log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k)); + size_t k = sym_ZSTD_decompressStream(dctx, &output, &input); + if (sym_ZSTD_isError(k)) { + log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k)); return zstd_ret_to_errno(k); } assert(output.pos >= size); @@ -351,11 +507,6 @@ int decompress_startswith_xz( size_t prefix_len, uint8_t extra) { -#if HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; - size_t allocated; - lzma_ret ret; - /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to * follow the prefix */ @@ -364,7 +515,17 @@ int decompress_startswith_xz( assert(buffer); assert(prefix); - ret = lzma_stream_decoder(&s, UINT64_MAX, 0); +#if HAVE_XZ + _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT; + size_t allocated; + lzma_ret ret; + int r; + + r = dlopen_lzma(); + if (r < 0) + return r; + + ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0); if (ret != LZMA_OK) return -EBADMSG; @@ -380,7 +541,7 @@ int decompress_startswith_xz( s.avail_out = allocated; for (;;) { - ret = lzma_code(&s, LZMA_FINISH); + ret = sym_lzma_code(&s, LZMA_FINISH); if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) return -EBADMSG; @@ -414,18 +575,22 @@ int decompress_startswith_lz4( size_t prefix_len, uint8_t extra) { -#if HAVE_LZ4 /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to * follow the prefix */ - size_t allocated; - int r; - assert(src); assert(src_size > 0); assert(buffer); assert(prefix); +#if HAVE_LZ4 + size_t allocated; + int r; + + r = dlopen_lz4(); + if (r < 0) + return r; + if (src_size <= 8) return -EBADMSG; @@ -433,7 +598,7 @@ int decompress_startswith_lz4( return -ENOMEM; allocated = MALLOC_SIZEOF_SAFE(*buffer); - r = LZ4_decompress_safe_partial( + r = sym_LZ4_decompress_safe_partial( (char*)src + 8, *buffer, src_size - 8, @@ -447,7 +612,7 @@ int decompress_startswith_lz4( if (r < 0 || (size_t) r < prefix_len + 1) { size_t size; - if (LZ4_versionNumber() >= 10803) + if (sym_LZ4_versionNumber() >= 10803) /* We trust that the newer lz4 decompresses the number of bytes we * requested if available in the compressed string. */ return 0; @@ -482,24 +647,31 @@ int decompress_startswith_zstd( const void *prefix, size_t prefix_len, uint8_t extra) { -#if HAVE_ZSTD + assert(src); assert(src_size > 0); assert(buffer); assert(prefix); - uint64_t size = ZSTD_getFrameContentSize(src, src_size); +#if HAVE_ZSTD + int r; + + r = dlopen_zstd(); + if (r < 0) + return r; + + uint64_t size = sym_ZSTD_getFrameContentSize(src, src_size); if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN)) return -EBADMSG; if (size < prefix_len + 1) return 0; /* Decompressed text too short to match the prefix and extra */ - _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx(); + _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx(); if (!dctx) return -ENOMEM; - if (!(greedy_realloc(buffer, MAX(ZSTD_DStreamOutSize(), prefix_len + 1), 1))) + if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1))) return -ENOMEM; ZSTD_inBuffer input = { @@ -512,9 +684,9 @@ int decompress_startswith_zstd( }; size_t k; - k = ZSTD_decompressStream(dctx, &output, &input); - if (ZSTD_isError(k)) { - log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k)); + k = sym_ZSTD_decompressStream(dctx, &output, &input); + if (sym_ZSTD_isError(k)) { + log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k)); return zstd_ret_to_errno(k); } assert(output.pos >= prefix_len + 1); @@ -559,16 +731,21 @@ int decompress_startswith( } int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) { + assert(fdf >= 0); + assert(fdt >= 0); + #if HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT; lzma_ret ret; uint8_t buf[BUFSIZ], out[BUFSIZ]; lzma_action action = LZMA_RUN; + int r; - assert(fdf >= 0); - assert(fdt >= 0); + r = dlopen_lzma(); + if (r < 0) + return r; - ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); + ret = sym_lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); if (ret != LZMA_OK) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize XZ encoder: code %u", @@ -603,7 +780,7 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncom s.avail_out = sizeof(out); } - ret = lzma_code(&s, action); + ret = sym_lzma_code(&s, action); if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Compression failed: code %u", @@ -641,7 +818,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco #if HAVE_LZ4 LZ4F_errorCode_t c; - _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL; + _cleanup_(sym_LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL; _cleanup_free_ void *in_buff = NULL; _cleanup_free_ char *out_buff = NULL; size_t out_allocsize, n, offset = 0, frame_size; @@ -651,11 +828,15 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco .frameInfo.blockSizeID = 5, }; - c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); - if (LZ4F_isError(c)) + r = dlopen_lz4(); + if (r < 0) + return r; + + c = sym_LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + if (sym_LZ4F_isError(c)) return -ENOMEM; - frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences); + frame_size = sym_LZ4F_compressBound(LZ4_BUFSIZE, &preferences); out_allocsize = frame_size + 64*1024; /* add some space for header and trailer */ out_buff = malloc(out_allocsize); if (!out_buff) @@ -665,8 +846,8 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco if (!in_buff) return -ENOMEM; - n = offset = total_out = LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences); - if (LZ4F_isError(n)) + n = offset = total_out = sym_LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences); + if (sym_LZ4F_isError(n)) return -EINVAL; log_debug("Buffer size is %zu bytes, header size %zu bytes.", out_allocsize, n); @@ -679,9 +860,9 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco return k; if (k == 0) break; - n = LZ4F_compressUpdate(ctx, out_buff + offset, out_allocsize - offset, + n = sym_LZ4F_compressUpdate(ctx, out_buff + offset, out_allocsize - offset, in_buff, k, NULL); - if (LZ4F_isError(n)) + if (sym_LZ4F_isError(n)) return -ENOTRECOVERABLE; total_in += k; @@ -700,8 +881,8 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco } } - n = LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL); - if (LZ4F_isError(n)) + n = sym_LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL); + if (sym_LZ4F_isError(n)) return -ENOTRECOVERABLE; offset += n; @@ -724,18 +905,22 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco } int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { + assert(fdf >= 0); + assert(fdt >= 0); #if HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT; lzma_ret ret; uint8_t buf[BUFSIZ], out[BUFSIZ]; lzma_action action = LZMA_RUN; + int r; - assert(fdf >= 0); - assert(fdt >= 0); + r = dlopen_lzma(); + if (r < 0) + return r; - ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0); if (ret != LZMA_OK) return log_debug_errno(SYNTHETIC_ERRNO(ENOMEM), "Failed to initialize XZ decoder: code %u", @@ -761,7 +946,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { s.avail_out = sizeof(out); } - ret = lzma_code(&s, action); + ret = sym_lzma_code(&s, action); if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Decompression failed: code %u", @@ -801,15 +986,19 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { #if HAVE_LZ4 size_t c; - _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL; + _cleanup_(sym_LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL; _cleanup_free_ char *buf = NULL; char *src; struct stat st; - int r = 0; + int r; size_t total_in = 0, total_out = 0; - c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); - if (LZ4F_isError(c)) + r = dlopen_lz4(); + if (r < 0) + return r; + + c = sym_LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); + if (sym_LZ4F_isError(c)) return -ENOMEM; if (fstat(in, &st) < 0) @@ -830,8 +1019,8 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { size_t produced = LZ4_BUFSIZE; size_t used = st.st_size - total_in; - c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL); - if (LZ4F_isError(c)) { + c = sym_LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL); + if (sym_LZ4F_isError(c)) { r = -EBADMSG; goto cleanup; } @@ -853,6 +1042,7 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", total_in, total_out, total_in > 0 ? (double) total_out / total_in * 100 : 0.0); + r = 0; cleanup: munmap(src, st.st_size); return r; @@ -863,28 +1053,33 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { } int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) { + assert(fdf >= 0); + assert(fdt >= 0); + #if HAVE_ZSTD - _cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL; + _cleanup_(sym_ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL; _cleanup_free_ void *in_buff = NULL, *out_buff = NULL; size_t in_allocsize, out_allocsize; size_t z; uint64_t left = max_bytes, in_bytes = 0; + int r; - assert(fdf >= 0); - assert(fdt >= 0); + r = dlopen_zstd(); + if (r < 0) + return r; /* Create the context and buffers */ - in_allocsize = ZSTD_CStreamInSize(); - out_allocsize = ZSTD_CStreamOutSize(); + in_allocsize = sym_ZSTD_CStreamInSize(); + out_allocsize = sym_ZSTD_CStreamOutSize(); in_buff = malloc(in_allocsize); out_buff = malloc(out_allocsize); - cctx = ZSTD_createCCtx(); + cctx = sym_ZSTD_createCCtx(); if (!cctx || !out_buff || !in_buff) return -ENOMEM; - z = ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1); - if (ZSTD_isError(z)) - log_debug("Failed to enable ZSTD checksum, ignoring: %s", ZSTD_getErrorName(z)); + z = sym_ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1); + if (sym_ZSTD_isError(z)) + log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z)); /* This loop read from the input file, compresses that entire chunk, * and writes all output produced to the output file. @@ -919,12 +1114,12 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unc * output to the file so we can reuse the buffer next * iteration. */ - remaining = ZSTD_compressStream2( + remaining = sym_ZSTD_compressStream2( cctx, &output, &input, is_last_chunk ? ZSTD_e_end : ZSTD_e_continue); - if (ZSTD_isError(remaining)) { - log_debug("ZSTD encoder failed: %s", ZSTD_getErrorName(remaining)); + if (sym_ZSTD_isError(remaining)) { + log_debug("ZSTD encoder failed: %s", sym_ZSTD_getErrorName(remaining)); return zstd_ret_to_errno(remaining); } @@ -968,22 +1163,26 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unc } int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) { + assert(fdf >= 0); + assert(fdt >= 0); + #if HAVE_ZSTD - _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL; + _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL; _cleanup_free_ void *in_buff = NULL, *out_buff = NULL; size_t in_allocsize, out_allocsize; size_t last_result = 0; uint64_t left = max_bytes, in_bytes = 0; + int r; - assert(fdf >= 0); - assert(fdt >= 0); - + r = dlopen_zstd(); + if (r < 0) + return r; /* Create the context and buffers */ - in_allocsize = ZSTD_DStreamInSize(); - out_allocsize = ZSTD_DStreamOutSize(); + in_allocsize = sym_ZSTD_DStreamInSize(); + out_allocsize = sym_ZSTD_DStreamOutSize(); in_buff = malloc(in_allocsize); out_buff = malloc(out_allocsize); - dctx = ZSTD_createDCtx(); + dctx = sym_ZSTD_createDCtx(); if (!dctx || !out_buff || !in_buff) return -ENOMEM; @@ -1032,8 +1231,8 @@ int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) { * for instance if the last decompression call returned * an error. */ - last_result = ZSTD_decompressStream(dctx, &output, &input); - if (ZSTD_isError(last_result)) { + last_result = sym_ZSTD_decompressStream(dctx, &output, &input); + if (sym_ZSTD_isError(last_result)) { has_error = true; break; } @@ -1059,7 +1258,7 @@ int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) { * on a frame, but we reached the end of the file! We assume * this is an error, and the input was truncated. */ - log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(last_result)); + log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(last_result)); return zstd_ret_to_errno(last_result); } diff --git a/src/basic/compress.h b/src/basic/compress.h index 1b5c645..d15c189 100644 --- a/src/basic/compress.h +++ b/src/basic/compress.h @@ -6,6 +6,13 @@ #include <stdint.h> #include <unistd.h> +#if HAVE_LZ4 +#include <lz4.h> +#include <lz4frame.h> +#endif + +#include "dlfcn-util.h" + typedef enum Compression { COMPRESSION_NONE, COMPRESSION_XZ, @@ -63,6 +70,23 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_size); int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size); int decompress_stream_zstd(int fdf, int fdt, uint64_t max_size); +#if HAVE_LZ4 +DLSYM_PROTOTYPE(LZ4_compress_default); +DLSYM_PROTOTYPE(LZ4_decompress_safe); +DLSYM_PROTOTYPE(LZ4_decompress_safe_partial); +DLSYM_PROTOTYPE(LZ4_versionNumber); + +int dlopen_lz4(void); +#endif + +#if HAVE_ZSTD +int dlopen_zstd(void); +#endif + +#if HAVE_XZ +int dlopen_lzma(void); +#endif + static inline int compress_blob( Compression compression, const void *src, uint64_t src_size, diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index a56f82f..7fdcc71 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -9,7 +9,10 @@ #include "conf-files.h" #include "constants.h" #include "dirent-util.h" +#include "errno-util.h" #include "fd-util.h" +#include "fileio.h" +#include "glyph-util.h" #include "hashmap.h" #include "log.h" #include "macro.h" @@ -366,9 +369,103 @@ int conf_files_list_dropins( assert(dirs); suffix = strjoina("/", dropin_dirname); - r = strv_extend_strv_concat(&dropin_dirs, (char**) dirs, suffix); + r = strv_extend_strv_concat(&dropin_dirs, dirs, suffix); if (r < 0) return r; return conf_files_list_strv(ret, ".conf", root, 0, (const char* const*) dropin_dirs); } + +/** + * Open and read a config file. + * + * The <fn> argument may be: + * - '-', meaning stdin. + * - a file name without a path. In this case <config_dirs> are searched. + * - a path, either relative or absolute. In this case <fn> is opened directly. + * + * This method is only suitable for configuration files which have a flat layout without dropins. + */ +int conf_file_read( + const char *root, + const char **config_dirs, + const char *fn, + parse_line_t parse_line, + void *userdata, + bool ignore_enoent, + bool *invalid_config) { + + _cleanup_fclose_ FILE *_f = NULL; + _cleanup_free_ char *_fn = NULL; + unsigned v = 0; + FILE *f; + int r = 0; + + assert(fn); + + if (streq(fn, "-")) { + f = stdin; + fn = "<stdin>"; + + log_debug("Reading config from stdin%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS)); + + } else if (is_path(fn)) { + r = path_make_absolute_cwd(fn, &_fn); + if (r < 0) + return log_error_errno(r, "Failed to make path absolute: %m"); + fn = _fn; + + f = _f = fopen(fn, "re"); + if (!_f) + r = -errno; + else + log_debug("Reading config file \"%s\"%s", fn, special_glyph(SPECIAL_GLYPH_ELLIPSIS)); + + } else { + r = search_and_fopen(fn, "re", root, config_dirs, &_f, &_fn); + if (r >= 0) { + f = _f; + fn = _fn; + log_debug("Reading config file \"%s\"%s", fn, special_glyph(SPECIAL_GLYPH_ELLIPSIS)); + } + } + + if (r == -ENOENT && ignore_enoent) { + log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); + return 0; /* No error, but nothing happened. */ + } + if (r < 0) + return log_error_errno(r, "Failed to read '%s': %m", fn); + + r = 1; /* We entered the part where we may modify state. */ + + for (;;) { + _cleanup_free_ char *line = NULL; + bool invalid_line = false; + int k; + + k = read_stripped_line(f, LONG_LINE_MAX, &line); + if (k < 0) + return log_error_errno(k, "Failed to read '%s': %m", fn); + if (k == 0) + break; + + v++; + + if (IN_SET(line[0], 0, '#')) + continue; + + k = parse_line(fn, v, line, invalid_config ? &invalid_line : NULL, userdata); + if (k < 0 && invalid_line) + /* Allow reporting with a special code if the caller requested this. */ + *invalid_config = true; + else + /* The first error, if any, becomes our return value. */ + RET_GATHER(r, k); + } + + if (ferror(f)) + RET_GATHER(r, log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read from file %s.", fn)); + + return r; +} diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index 566cc8f..cf89ee6 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <stdbool.h> + #include "macro.h" enum { @@ -29,3 +31,19 @@ int conf_files_list_dropins( const char *dropin_dirname, const char *root, const char * const *dirs); + +typedef int parse_line_t( + const char *fname, + unsigned line, + const char *buffer, + bool *invalid_config, + void *userdata); + +int conf_file_read( + const char *root, + const char **config_dirs, + const char *fn, + parse_line_t parse_line, + void *userdata, + bool ignore_enoent, + bool *invalid_config); diff --git a/src/basic/constants.h b/src/basic/constants.h index 6bb5f3c..e70817c 100644 --- a/src/basic/constants.h +++ b/src/basic/constants.h @@ -42,9 +42,6 @@ #define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC) #define DEFAULT_START_LIMIT_BURST 5 -/* Wait for 1.5 seconds at maximum for freeze operation */ -#define FREEZE_TIMEOUT (1500 * USEC_PER_MSEC) - /* The default time after which exit-on-idle services exit. This * should be kept lower than the watchdog timeout, because otherwise * the watchdog pings will keep the loop busy. */ @@ -67,18 +64,12 @@ "/usr/local/lib/" n "\0" \ "/usr/lib/" n "\0" -#define CONF_PATHS_USR(n) \ +#define CONF_PATHS(n) \ "/etc/" n, \ "/run/" n, \ "/usr/local/lib/" n, \ "/usr/lib/" n -#define CONF_PATHS(n) \ - CONF_PATHS_USR(n) - -#define CONF_PATHS_USR_STRV(n) \ - STRV_MAKE(CONF_PATHS_USR(n)) - #define CONF_PATHS_STRV(n) \ STRV_MAKE(CONF_PATHS(n)) diff --git a/src/basic/devnum-util.c b/src/basic/devnum-util.c index f82e13b..652740c 100644 --- a/src/basic/devnum-util.c +++ b/src/basic/devnum-util.c @@ -58,21 +58,18 @@ int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) { } int device_path_make_inaccessible(mode_t mode, char **ret) { - char *s; + const char *s; assert(ret); if (S_ISCHR(mode)) - s = strdup("/run/systemd/inaccessible/chr"); + s = "/run/systemd/inaccessible/chr"; else if (S_ISBLK(mode)) - s = strdup("/run/systemd/inaccessible/blk"); + s = "/run/systemd/inaccessible/blk"; else return -ENODEV; - if (!s) - return -ENOMEM; - *ret = s; - return 0; + return strdup_to(ret, s); } int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) { diff --git a/src/basic/dlfcn-util.c b/src/basic/dlfcn-util.c new file mode 100644 index 0000000..8022f55 --- /dev/null +++ b/src/basic/dlfcn-util.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "dlfcn-util.h" + +static int dlsym_many_or_warnv(void *dl, int log_level, va_list ap) { + void (**fn)(void); + + /* Tries to resolve a bunch of function symbols, and logs an error about if it cannot resolve one of + * them. Note that this function possibly modifies the supplied function pointers if the whole + * operation fails. */ + + while ((fn = va_arg(ap, typeof(fn)))) { + void (*tfn)(void); + const char *symbol; + + symbol = va_arg(ap, typeof(symbol)); + + tfn = (typeof(tfn)) dlsym(dl, symbol); + if (!tfn) + return log_full_errno(log_level, + SYNTHETIC_ERRNO(ELIBBAD), + "Can't find symbol %s: %s", symbol, dlerror()); + *fn = tfn; + } + + return 0; +} + +int dlsym_many_or_warn_sentinel(void *dl, int log_level, ...) { + va_list ap; + int r; + + va_start(ap, log_level); + r = dlsym_many_or_warnv(dl, log_level, ap); + va_end(ap); + + return r; +} + +int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_level, ...) { + _cleanup_(dlclosep) void *dl = NULL; + int r; + + if (*dlp) + return 0; /* Already loaded */ + + dl = dlopen(filename, RTLD_LAZY); + if (!dl) + 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); + va_end(ap); + + if (r < 0) + return r; + + /* Note that we never release the reference here, because there's no real reason to. After all this + * was traditionally a regular shared library dependency which lives forever too. */ + *dlp = TAKE_PTR(dl); + return 1; +} diff --git a/src/basic/dlfcn-util.h b/src/basic/dlfcn-util.h new file mode 100644 index 0000000..83ab320 --- /dev/null +++ b/src/basic/dlfcn-util.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <dlfcn.h> + +#include "macro.h" + +static inline void* safe_dlclose(void *dl) { + if (!dl) + return NULL; + + assert_se(dlclose(dl) == 0); + return NULL; +} + +static inline void dlclosep(void **dlp) { + safe_dlclose(*dlp); +} + +int dlsym_many_or_warn_sentinel(void *dl, int log_level, ...) _sentinel_; +int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_level, ...) _sentinel_; + +#define dlsym_many_or_warn(dl, log_level, ...) \ + dlsym_many_or_warn_sentinel(dl, log_level, __VA_ARGS__, NULL) +#define dlopen_many_sym_or_warn(dlp, filename, log_level, ...) \ + dlopen_many_sym_or_warn_sentinel(dlp, filename, log_level, __VA_ARGS__, NULL) + +#define DLSYM_PROTOTYPE(symbol) \ + extern typeof(symbol)* sym_##symbol +#define DLSYM_FUNCTION(symbol) \ + typeof(symbol)* sym_##symbol = NULL + +/* Macro useful for putting together variable/symbol name pairs when calling dlsym_many_or_warn(). Assumes + * that each library symbol to resolve will be placed in a variable with the "sym_" prefix, i.e. a symbol + * "foobar" is loaded into a variable "sym_foobar". */ +#define DLSYM_ARG(arg) \ + ({ assert_cc(__builtin_types_compatible_p(typeof(sym_##arg), typeof(&arg))); &sym_##arg; }), STRINGIFY(arg) + +/* libbpf is a bit confused about type-safety and API compatibility. Provide a macro that can tape over that mess. Sad. */ +#define DLSYM_ARG_FORCE(arg) \ + &sym_##arg, STRINGIFY(arg) + +#define ELF_NOTE_DLOPEN_VENDOR "FDO" +#define ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a) +#define ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required" +#define ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended" +#define ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested" + +/* Add an ".note.dlopen" ELF note to our binary that declares our weak dlopen() dependency. This + * information can be read from an ELF file via "readelf -p .note.dlopen" or an equivalent command. */ +#define _ELF_NOTE_DLOPEN(json, variable_name) \ + __attribute__((used, section(".note.dlopen"))) _Alignas(sizeof(uint32_t)) static const struct { \ + struct { \ + uint32_t n_namesz, n_descsz, n_type; \ + } nhdr; \ + char name[sizeof(ELF_NOTE_DLOPEN_VENDOR)]; \ + _Alignas(sizeof(uint32_t)) char dlopen_json[sizeof(json)]; \ + } variable_name = { \ + .nhdr = { \ + .n_namesz = sizeof(ELF_NOTE_DLOPEN_VENDOR), \ + .n_descsz = sizeof(json), \ + .n_type = ELF_NOTE_DLOPEN_TYPE, \ + }, \ + .name = ELF_NOTE_DLOPEN_VENDOR, \ + .dlopen_json = json, \ + } + +#define _SONAME_ARRAY1(a) "[\""a"\"]" +#define _SONAME_ARRAY2(a, b) "[\""a"\",\""b"\"]" +#define _SONAME_ARRAY3(a, b, c) "[\""a"\",\""b"\",\""c"\"]" +#define _SONAME_ARRAY4(a, b, c, d) "[\""a"\",\""b"\",\""c"\"",\""d"\"]" +#define _SONAME_ARRAY5(a, b, c, d, e) "[\""a"\",\""b"\",\""c"\"",\""d"\",\""e"\"]" +#define _SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME +#define _SONAME_ARRAY(...) _SONAME_ARRAY_GET(__VA_ARGS__, _SONAME_ARRAY5, _SONAME_ARRAY4, _SONAME_ARRAY3, _SONAME_ARRAY2, _SONAME_ARRAY1)(__VA_ARGS__) + +/* The 'priority' must be one of 'required', 'recommended' or 'suggested' as per specification, use the + * macro defined above to specify it. + * Multiple sonames can be passed and they will be automatically constructed into a json array (but note that + * due to preprocessor language limitations if more than the limit defined above is used, a new + * _SONAME_ARRAY<X+1> will need to be added). */ +#define ELF_NOTE_DLOPEN(feature, description, priority, ...) \ + _ELF_NOTE_DLOPEN("[{\"feature\":\"" feature "\",\"description\":\"" description "\",\"priority\":\"" priority "\",\"soname\":" _SONAME_ARRAY(__VA_ARGS__) "}]", UNIQ_T(s, UNIQ)) diff --git a/src/basic/efivars.c b/src/basic/efivars.c index 9011ae2..8470d08 100644 --- a/src/basic/efivars.c +++ b/src/basic/efivars.c @@ -177,12 +177,13 @@ static int efi_verify_variable(const char *variable, uint32_t attr, const void * } int efi_set_variable(const char *variable, const void *value, size_t size) { + static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + struct var { uint32_t attr; char buf[]; } _packed_ * _cleanup_free_ buf = NULL; _cleanup_close_ int fd = -EBADF; - uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; bool saved_flags_valid = false; unsigned saved_flags; int r; @@ -190,14 +191,14 @@ int efi_set_variable(const char *variable, const void *value, size_t size) { assert(variable); assert(value || size == 0); - const char *p = strjoina("/sys/firmware/efi/efivars/", variable); - /* size 0 means removal, empty variable would not be enough for that */ if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) { log_debug("Variable '%s' is already in wanted state, skipping write.", variable); return 0; } + const char *p = strjoina("/sys/firmware/efi/efivars/", variable); + /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default, * to protect them for accidental removal and modification. We are not changing these variables * accidentally however, hence let's unset the bit first. */ @@ -238,10 +239,7 @@ int efi_set_variable(const char *variable, const void *value, size_t size) { /* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is * useful for processes that cache EFI variables to detect when changes occurred. */ - if (futimens(fd, (struct timespec[2]) { - { .tv_nsec = UTIME_NOW }, - { .tv_nsec = UTIME_NOW } - }) < 0) + if (futimens(fd, /* times = */ NULL) < 0) log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p); r = 0; @@ -398,16 +396,8 @@ int systemd_efi_options_variable(char **ret) { /* For testing purposes it is sometimes useful to be able to override this */ e = secure_getenv("SYSTEMD_EFI_OPTIONS"); - if (e) { - char *m; - - m = strdup(e); - if (!m) - return -ENOMEM; - - *ret = m; - return 0; - } + if (e) + return strdup_to(ret, e); r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), ret); if (r == -ENOENT) diff --git a/src/basic/env-file.c b/src/basic/env-file.c index c2cbff4..2fff98f 100644 --- a/src/basic/env-file.c +++ b/src/basic/env-file.c @@ -125,7 +125,7 @@ static int parse_env_file_internal( state = VALUE; if (!GREEDY_REALLOC(value, n_value+2)) - return -ENOMEM; + return -ENOMEM; value[n_value++] = c; } diff --git a/src/basic/env-util.c b/src/basic/env-util.c index a97651d..9e74ba0 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -18,6 +18,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "syslog-util.h" #include "utf8.h" /* We follow bash for the character set. Different shells have different rules. */ @@ -244,9 +245,9 @@ static bool env_match(const char *t, const char *pattern) { return true; if (!strchr(pattern, '=')) { - size_t l = strlen(pattern); + t = startswith(t, pattern); - return strneq(t, pattern, l) && t[l] == '='; + return t && *t == '='; } return false; @@ -309,19 +310,17 @@ char **strv_env_delete(char **x, size_t n_lists, ...) { return TAKE_PTR(t); } -char **strv_env_unset(char **l, const char *p) { - char **f, **t; +char** strv_env_unset(char **l, const char *p) { + assert(p); if (!l) return NULL; - assert(p); - /* Drops every occurrence of the env var setting p in the * string list. Edits in-place. */ + char **f, **t; for (f = t = l; *f; f++) { - if (env_match(*f, p)) { free(*f); continue; @@ -334,14 +333,13 @@ char **strv_env_unset(char **l, const char *p) { return l; } -char **strv_env_unset_many(char **l, ...) { - char **f, **t; - +char** strv_env_unset_many_internal(char **l, ...) { if (!l) return NULL; /* Like strv_env_unset() but applies many at once. Edits in-place. */ + char **f, **t; for (f = t = l; *f; f++) { bool found = false; const char *p; @@ -349,12 +347,11 @@ char **strv_env_unset_many(char **l, ...) { va_start(ap, l); - while ((p = va_arg(ap, const char*))) { + while ((p = va_arg(ap, const char*))) if (env_match(*f, p)) { found = true; break; } - } va_end(ap); @@ -458,6 +455,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) { return strv_env_replace_consume(l, p); } +int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) { + int r; + + assert(l); + assert(key); + + if (!env_name_is_valid(key)) + return -EINVAL; + + if (!valuef) { + strv_env_unset(*l, key); + return 0; + } + + _cleanup_free_ char *value = NULL; + va_list ap; + va_start(ap, valuef); + r = vasprintf(&value, valuef, ap); + va_end(ap); + if (r < 0) + return -ENOMEM; + + char *p = strjoin(key, "=", value); + if (!p) + return -ENOMEM; + + return strv_env_replace_consume(l, p); +} + int _strv_env_assign_many(char ***l, ...) { va_list ap; int r; @@ -500,18 +526,17 @@ int _strv_env_assign_many(char ***l, ...) { return 0; } -char *strv_env_get_n(char **l, const char *name, size_t k, ReplaceEnvFlags flags) { +char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) { assert(name); if (k == SIZE_MAX) - k = strlen_ptr(name); + k = strlen(name); if (k <= 0) return NULL; STRV_FOREACH_BACKWARDS(i, l) - if (strneq(*i, name, k) && - (*i)[k] == '=') - return *i + k + 1; + if (strneq(*i, name, k) && (*i)[k] == '=') + return (char*) *i + k + 1; if (flags & REPLACE_ENV_USE_ENVIRONMENT) { const char *t; @@ -654,7 +679,7 @@ int replace_env_full( pu = ret_unset_variables ? &unset_variables : NULL; pb = ret_bad_variables ? &bad_variables : NULL; - for (e = format, i = 0; *e && i < n; e ++, i ++) + for (e = format, i = 0; *e && i < n; e++, i++) switch (state) { case WORD: @@ -938,7 +963,7 @@ int getenv_bool(const char *p) { return parse_boolean(e); } -int getenv_bool_secure(const char *p) { +int secure_getenv_bool(const char *p) { const char *e; e = secure_getenv(p); @@ -948,7 +973,7 @@ int getenv_bool_secure(const char *p) { return parse_boolean(e); } -int getenv_uint64_secure(const char *p, uint64_t *ret) { +int secure_getenv_uint64(const char *p, uint64_t *ret) { const char *e; assert(p); @@ -1002,6 +1027,17 @@ int setenv_systemd_exec_pid(bool update_only) { return 1; } +int setenv_systemd_log_level(void) { + _cleanup_free_ char *val = NULL; + int r; + + r = log_level_to_string_alloc(log_get_max_level(), &val); + if (r < 0) + return r; + + return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true)); +} + int getenv_path_list(const char *name, char ***ret_paths) { _cleanup_strv_free_ char **l = NULL; const char *e; @@ -1104,9 +1140,7 @@ int setenvf(const char *name, bool overwrite, const char *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) diff --git a/src/basic/env-util.h b/src/basic/env-util.h index 34cf1f9..6610ca8 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -43,26 +43,28 @@ char** _strv_env_merge(char **first, ...); #define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX) char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ -char **strv_env_unset(char **l, const char *p); /* In place ... */ -char **strv_env_unset_many(char **l, ...) _sentinel_; +char** strv_env_unset(char **l, const char *p); /* In place ... */ +char** strv_env_unset_many_internal(char **l, ...) _sentinel_; +#define strv_env_unset_many(l, ...) strv_env_unset_many_internal(l, __VA_ARGS__, NULL) int strv_env_replace_consume(char ***l, char *p); /* In place ... */ int strv_env_replace_strdup(char ***l, const char *assignment); int strv_env_replace_strdup_passthrough(char ***l, const char *assignment); int strv_env_assign(char ***l, const char *key, const char *value); +int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4); int _strv_env_assign_many(char ***l, ...) _sentinel_; #define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL) -char *strv_env_get_n(char **l, const char *name, size_t k, ReplaceEnvFlags flags) _pure_; -static inline char *strv_env_get(char **x, const char *n) { +char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags); +static inline char* strv_env_get(char * const *x, const char *n) { return strv_env_get_n(x, n, SIZE_MAX, 0); } char *strv_env_pairs_get(char **l, const char *name) _pure_; int getenv_bool(const char *p); -int getenv_bool_secure(const char *p); +int secure_getenv_bool(const char *p); -int getenv_uint64_secure(const char *p, uint64_t *ret); +int secure_getenv_uint64(const char *p, uint64_t *ret); /* Like setenv, but calls unsetenv if value == NULL. */ int set_unset_env(const char *name, const char *value, bool overwrite); @@ -71,6 +73,7 @@ int set_unset_env(const char *name, const char *value, bool overwrite); int putenv_dup(const char *assignment, bool override); int setenv_systemd_exec_pid(bool update_only); +int setenv_systemd_log_level(void); /* Parses and does sanity checks on an environment variable containing * PATH-like colon-separated absolute paths */ diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h index 27804e6..48b76e4 100644 --- a/src/basic/errno-util.h +++ b/src/basic/errno-util.h @@ -167,7 +167,8 @@ static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) { -EAFNOSUPPORT, -EPFNOSUPPORT, -EPROTONOSUPPORT, - -ESOCKTNOSUPPORT); + -ESOCKTNOSUPPORT, + -ENOPROTOOPT); } _DEFINE_ABS_WRAPPER(NOT_SUPPORTED); diff --git a/src/basic/escape.c b/src/basic/escape.c index 75a1d68..2067be4 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -451,6 +451,12 @@ char* octescape(const char *s, size_t len) { assert(s || len == 0); + if (len == SIZE_MAX) + len = strlen(s); + + if (len > (SIZE_MAX - 1) / 4) + return NULL; + t = buf = new(char, len * 4 + 1); if (!buf) return NULL; @@ -471,6 +477,33 @@ char* octescape(const char *s, size_t len) { return buf; } +char* decescape(const char *s, const char *bad, size_t len) { + char *buf, *t; + + /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */ + + assert(s || len == 0); + + t = buf = new(char, len * 4 + 1); + if (!buf) + return NULL; + + for (size_t i = 0; i < len; i++) { + uint8_t u = (uint8_t) s[i]; + + if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) { + *(t++) = '\\'; + *(t++) = '0' + (u / 100); + *(t++) = '0' + ((u / 10) % 10); + *(t++) = '0' + (u % 10); + } else + *(t++) = u; + } + + *t = 0; + return buf; +} + static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) { assert(bad); assert(t); diff --git a/src/basic/escape.h b/src/basic/escape.h index 318da6f..65caf0d 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -65,6 +65,7 @@ static inline char* xescape(const char *s, const char *bad) { return xescape_full(s, bad, SIZE_MAX, 0); } char* octescape(const char *s, size_t len); +char* decescape(const char *s, const char *bad, size_t len); char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags); char* shell_escape(const char *s, const char *bad); diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index 0a6a54f..4bf91f6 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -59,8 +59,8 @@ void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) { assert(p); assert(state); - siphash24_compress(&p->length, sizeof(p->length), state); - siphash24_compress(p->bytes, p->length, state); + siphash24_compress_typesafe(p->length, state); + siphash24_compress_safe(p->bytes, p->length, state); } DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare); @@ -106,7 +106,7 @@ int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) { } static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) { - siphash24_compress(p, sizeof(struct ether_addr), state); + siphash24_compress_typesafe(*p, state); } DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare); @@ -270,3 +270,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) { *ret = a.ether; return 0; } + +void ether_addr_mark_random(struct ether_addr *addr) { + assert(addr); + + /* see eth_random_addr in the kernel */ + addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ + addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ +} diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h index 83ed77d..187e4ef 100644 --- a/src/basic/ether-addr-util.h +++ b/src/basic/ether-addr-util.h @@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) { extern const struct hash_ops ether_addr_hash_ops; extern const struct hash_ops ether_addr_hash_ops_free; + +void ether_addr_mark_random(struct ether_addr *addr); diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index 160f771..012cee6 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -244,52 +244,43 @@ int extract_first_word_and_warn( * Let's make sure that ExtractFlags fits into an unsigned int. */ assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned)); -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) { +int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) { va_list ap; - char **l; - int n = 0, i, c, r; + unsigned n = 0; + int r; - /* Parses a number of words from a string, stripping any - * quotes if necessary. */ + /* Parses a number of words from a string, stripping any quotes if necessary. */ assert(p); /* Count how many words are expected */ va_start(ap, flags); - for (;;) { - if (!va_arg(ap, char **)) - break; + while (va_arg(ap, char**)) n++; - } va_end(ap); - if (n <= 0) + if (n == 0) return 0; /* Read all words into a temporary array */ - l = newa0(char*, n); - for (c = 0; c < n; c++) { + char **l = newa0(char*, n); + unsigned c; + for (c = 0; c < n; c++) { r = extract_first_word(p, &l[c], separators, flags); if (r < 0) { free_many_charp(l, c); return r; } - if (r == 0) break; } - /* If we managed to parse all words, return them in the passed - * in parameters */ + /* If we managed to parse all words, return them in the passed in parameters */ va_start(ap, flags); - for (i = 0; i < n; i++) { - char **v; - - v = va_arg(ap, char **); - assert(v); - - *v = l[i]; + FOREACH_ARRAY(i, l, n) { + char **v = ASSERT_PTR(va_arg(ap, char**)); + *v = *i; } va_end(ap); diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h index c82ad76..da4f6ae 100644 --- a/src/basic/extract-word.h +++ b/src/basic/extract-word.h @@ -19,4 +19,7 @@ typedef enum ExtractFlags { int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue); -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_; + +int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) _sentinel_; +#define extract_many_words(p, separators, flags, ...) \ + extract_many_words_internal(p, separators, flags, ##__VA_ARGS__, NULL) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 542acca..da4ee63 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -167,7 +167,23 @@ int fd_nonblock(int fd, bool nonblock) { if (nflags == flags) return 0; - return RET_NERRNO(fcntl(fd, F_SETFL, nflags)); + if (fcntl(fd, F_SETFL, nflags) < 0) + return -errno; + + return 1; +} + +int stdio_disable_nonblock(void) { + int ret = 0; + + /* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as + * write()s might unexpectedly fail with EAGAIN. */ + + RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false)); + RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false)); + RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false)); + + return ret; } int fd_cloexec(int fd, bool cloexec) { @@ -451,6 +467,53 @@ int close_all_fds(const int except[], size_t n_except) { return r; } +int pack_fds(int fds[], size_t n_fds) { + if (n_fds <= 0) + return 0; + + /* Shifts around the fds in the provided array such that they + * all end up packed next to each-other, in order, starting + * from SD_LISTEN_FDS_START. This must be called after close_all_fds(); + * it is likely to freeze up otherwise. You should probably use safe_fork_full + * with FORK_CLOSE_ALL_FDS|FORK_PACK_FDS set, to ensure that this is done correctly. + * The fds array is modified in place with the new FD numbers. */ + + assert(fds); + + for (int start = 0;;) { + int restart_from = -1; + + for (int i = start; i < (int) n_fds; i++) { + int nfd; + + /* Already at right index? */ + if (fds[i] == i + 3) + continue; + + nfd = fcntl(fds[i], F_DUPFD, i + 3); + if (nfd < 0) + return -errno; + + safe_close(fds[i]); + fds[i] = nfd; + + /* Hmm, the fd we wanted isn't free? Then + * let's remember that and try again from here */ + if (nfd != i + 3 && restart_from < 0) + restart_from = i; + } + + if (restart_from < 0) + break; + + start = restart_from; + } + + assert(fds[0] == 3); + + return 0; +} + int same_fd(int a, int b) { struct stat sta, stb; pid_t pid; @@ -809,6 +872,46 @@ int fd_reopen(int fd, int flags) { return new_fd; } +int fd_reopen_propagate_append_and_position(int fd, int flags) { + /* Invokes fd_reopen(fd, flags), but propagates O_APPEND if set on original fd, and also tries to + * keep current file position. + * + * You should use this if the original fd potentially is O_APPEND, otherwise we get rather + * "unexpected" behavior. Unless you intentionally want to overwrite pre-existing data, and have + * your output overwritten by the next user. + * + * Use case: "systemd-run --pty >> some-log". + * + * The "keep position" part is obviously nonsense for the O_APPEND case, but should reduce surprises + * if someone carefully pre-positioned the passed in original input or non-append output FDs. */ + + assert(fd >= 0); + assert(!(flags & (O_APPEND|O_DIRECTORY))); + + int existing_flags = fcntl(fd, F_GETFL); + if (existing_flags < 0) + return -errno; + + int new_fd = fd_reopen(fd, flags | (existing_flags & O_APPEND)); + if (new_fd < 0) + return new_fd; + + /* Try to adjust the offset, but ignore errors. */ + off_t p = lseek(fd, 0, SEEK_CUR); + if (p > 0) { + off_t new_p = lseek(new_fd, p, SEEK_SET); + if (new_p < 0) + log_debug_errno(errno, + "Failed to propagate file position for re-opened fd %d, ignoring: %m", + fd); + else if (new_p != p) + log_debug("Failed to propagate file position for re-opened fd %d (%lld != %lld), ignoring.", + fd, (long long) new_p, (long long) p); + } + + return new_fd; +} + int fd_reopen_condition( int fd, int flags, @@ -853,6 +956,38 @@ int fd_is_opath(int fd) { return FLAGS_SET(r, O_PATH); } +int fd_verify_safe_flags_full(int fd, int extra_flags) { + int flags, unexpected_flags; + + /* Check if an extrinsic fd is safe to work on (by a privileged service). This ensures that clients + * can't trick a privileged service into giving access to a file the client doesn't already have + * access to (especially via something like O_PATH). + * + * O_NOFOLLOW: For some reason the kernel will return this flag from fcntl(); it doesn't go away + * immediately after open(). It should have no effect whatsoever to an already-opened FD, + * and since we refuse O_PATH it should be safe. + * + * RAW_O_LARGEFILE: glibc secretly sets this and neglects to hide it from us if we call fcntl. + * See comment in missing_fcntl.h for more details about this. + * + * If 'extra_flags' is specified as non-zero the included flags are also allowed. + */ + + assert(fd >= 0); + + flags = fcntl(fd, F_GETFL); + if (flags < 0) + return -errno; + + unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags); + if (unexpected_flags != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO), + "Unexpected flags set for extrinsic fd: 0%o", + (unsigned) unexpected_flags); + + return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */ +} + int read_nr_open(void) { _cleanup_free_ char *nr_open = NULL; int r; @@ -899,10 +1034,7 @@ int fd_get_diskseq(int fd, uint64_t *ret) { } int path_is_root_at(int dir_fd, const char *path) { - STRUCT_NEW_STATX_DEFINE(st); - STRUCT_NEW_STATX_DEFINE(pst); - _cleanup_close_ int fd = -EBADF; - int r; + _cleanup_close_ int fd = -EBADF, pfd = -EBADF; assert(dir_fd >= 0 || dir_fd == AT_FDCWD); @@ -914,60 +1046,74 @@ int path_is_root_at(int dir_fd, const char *path) { dir_fd = fd; } - r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx); - if (r == -ENOTDIR) - return false; + pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC); + if (pfd < 0) + return errno == ENOTDIR ? false : -errno; + + /* Even if the parent directory has the same inode, the fd may not point to the root directory "/", + * and we also need to check that the mount ids are the same. Otherwise, a construct like the + * following could be used to trick us: + * + * $ mkdir /tmp/x /tmp/x/y + * $ mount --bind /tmp/x /tmp/x/y + */ + + return fds_are_same_mount(dir_fd, pfd); +} + +int fds_are_same_mount(int fd1, int fd2) { + STRUCT_NEW_STATX_DEFINE(st1); + STRUCT_NEW_STATX_DEFINE(st2); + int r; + + assert(fd1 >= 0); + assert(fd2 >= 0); + + r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx); if (r < 0) return r; - r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx); + r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx); if (r < 0) return r; /* First, compare inode. If these are different, the fd does not point to the root directory "/". */ - if (!statx_inode_same(&st.sx, &pst.sx)) + if (!statx_inode_same(&st1.sx, &st2.sx)) return false; - /* Even if the parent directory has the same inode, the fd may not point to the root directory "/", - * and we also need to check that the mount ids are the same. Otherwise, a construct like the - * following could be used to trick us: - * - * $ mkdir /tmp/x /tmp/x/y - * $ mount --bind /tmp/x /tmp/x/y - * - * Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old + /* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old * kernel is used. In that case, let's assume that we do not have such spurious mount points in an * early boot stage, and silently skip the following check. */ - if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) { + if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) { int mntid; - r = path_get_mnt_id_at_fallback(dir_fd, "", &mntid); + r = path_get_mnt_id_at_fallback(fd1, "", &mntid); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return true; /* skip the mount ID check */ if (r < 0) return r; assert(mntid >= 0); - st.nsx.stx_mnt_id = mntid; - st.nsx.stx_mask |= STATX_MNT_ID; + st1.nsx.stx_mnt_id = mntid; + st1.nsx.stx_mask |= STATX_MNT_ID; } - if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) { + if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) { int mntid; - r = path_get_mnt_id_at_fallback(dir_fd, "..", &mntid); + r = path_get_mnt_id_at_fallback(fd2, "", &mntid); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return true; /* skip the mount ID check */ if (r < 0) return r; assert(mntid >= 0); - pst.nsx.stx_mnt_id = mntid; - pst.nsx.stx_mask |= STATX_MNT_ID; + st2.nsx.stx_mnt_id = mntid; + st2.nsx.stx_mask |= STATX_MNT_ID; } - return statx_mount_same(&st.nsx, &pst.nsx); + return statx_mount_same(&st1.nsx, &st2.nsx); } const char *accmode_to_string(int flags) { diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index d3e9192..af17481 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -8,6 +8,7 @@ #include <sys/socket.h> #include "macro.h" +#include "missing_fcntl.h" #include "stdio-util.h" /* maximum length of fdname */ @@ -52,6 +53,11 @@ static inline void fclosep(FILE **f) { safe_fclose(*f); } +static inline void* close_fd_ptr(void *p) { + safe_close(PTR_TO_FD(p)); + return NULL; +} + DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL); @@ -62,6 +68,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL); #define _cleanup_close_pair_ _cleanup_(close_pairp) int fd_nonblock(int fd, bool nonblock); +int stdio_disable_nonblock(void); + int fd_cloexec(int fd, bool cloexec); int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec); @@ -70,6 +78,8 @@ int get_max_fd(void); int close_all_fds(const int except[], size_t n_except); int close_all_fds_without_malloc(const int except[], size_t n_except); +int pack_fds(int fds[], size_t n); + int same_fd(int a, int b); void cmsg_close_all(struct msghdr *mh); @@ -101,8 +111,16 @@ static inline int make_null_stdio(void) { }) int fd_reopen(int fd, int flags); +int fd_reopen_propagate_append_and_position(int fd, int flags); int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd); + int fd_is_opath(int fd); + +int fd_verify_safe_flags_full(int fd, int extra_flags); +static inline int fd_verify_safe_flags(int fd) { + return fd_verify_safe_flags_full(fd, 0); +} + int read_nr_open(void); int fd_get_diskseq(int fd, uint64_t *ret); @@ -117,6 +135,8 @@ static inline int dir_fd_is_root_or_cwd(int dir_fd) { return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL); } +int fds_are_same_mount(int fd1, int fd2); + /* The maximum length a buffer for a /proc/self/fd/<fd> path needs */ #define PROC_FD_PATH_MAX \ (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index a050b61..5233781 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -28,10 +28,11 @@ #include "stdio-util.h" #include "string-util.h" #include "sync-util.h" +#include "terminal-util.h" #include "tmpfile-util.h" /* The maximum size of the file we'll read in one go in read_full_file() (64M). */ -#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U) +#define READ_FULL_BYTES_MAX (64U * U64_MB - UINT64_C(1)) /* Used when a size is specified for read_full_file() with READ_FULL_FILE_UNBASE64 or _UNHEX */ #define READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY 3 @@ -44,7 +45,7 @@ * exponentially in a loop. We use a size limit of 4M-2 because 4M-1 is the maximum buffer that /proc/sys/ * allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we * can detect EOFs. */ -#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U) +#define READ_VIRTUAL_BYTES_MAX (4U * U64_MB - UINT64_C(2)) int fdopen_unlocked(int fd, const char *options, FILE **ret) { assert(ret); @@ -199,6 +200,19 @@ int write_string_stream_ts( return 0; } +static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) { + + /* We support three different modes, that are the ones that really make sense for text files like this: + * + * → 0600 (i.e. root-only) + * → 0444 (i.e. read-only) + * → 0644 (i.e. writable for root, readable for everyone else) + */ + + return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : + FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644; +} + static int write_string_file_atomic_at( int dir_fd, const char *fn, @@ -224,7 +238,7 @@ static int write_string_file_atomic_at( if (r < 0) goto fail; - r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644); + r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags)); if (r < 0) goto fail; @@ -287,7 +301,7 @@ int write_string_file_ts_at( (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY), - (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); + write_string_file_flags_to_mode(flags)); if (fd < 0) { r = -errno; goto fail; @@ -1313,33 +1327,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) { return 0; } -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) { - int r; - +int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space) { assert(s); + assert(space); - /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter - * when specified shall initially point to a boolean variable initialized to false. It is set to true after the - * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each - * element, but not before the first one. */ + /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. + * The *space parameter when specified shall initially point to a boolean variable initialized + * to false. It is set to true after the first invocation. This call is supposed to be use in loops, + * where a separator shall be inserted between each element, but not before the first one. */ if (!f) f = stdout; - if (space) { - if (!separator) - separator = " "; + if (!separator) + separator = " "; - if (*space) { - r = fputs(separator, f); - if (r < 0) - return r; - } + if (*space) + if (fputs(separator, f) < 0) + return -EIO; - *space = true; - } + *space = true; + + if (fputs(s, f) < 0) + return -EIO; - return fputs(s, f); + return 0; } /* A bitmask of the EOL markers we know */ @@ -1459,7 +1471,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { * and don't call isatty() on an invalid fd */ flags |= READ_LINE_NOT_A_TTY; else - flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY; + flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY; } if (FLAGS_SET(flags, READ_LINE_IS_A_TTY)) break; @@ -1492,7 +1504,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { int read_stripped_line(FILE *f, size_t limit, char **ret) { _cleanup_free_ char *s = NULL; - int r; + int r, k; assert(f); @@ -1501,23 +1513,17 @@ int read_stripped_line(FILE *f, size_t limit, char **ret) { return r; if (ret) { - const char *p; - - p = strstrip(s); + const char *p = strstrip(s); if (p == s) *ret = TAKE_PTR(s); else { - char *copy; - - copy = strdup(p); - if (!copy) - return -ENOMEM; - - *ret = copy; + k = strdup_to(ret, p); + if (k < 0) + return k; } } - return r; + return r > 0; /* Return 1 if something was read. */ } int safe_fgetc(FILE *f, char *ret) { diff --git a/src/basic/fileio.h b/src/basic/fileio.h index e0e0a45..03c3f3f 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -26,7 +26,8 @@ typedef enum { WRITE_STRING_FILE_NOFOLLOW = 1 << 8, WRITE_STRING_FILE_MKDIR_0755 = 1 << 9, WRITE_STRING_FILE_MODE_0600 = 1 << 10, - WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11, + WRITE_STRING_FILE_MODE_0444 = 1 << 11, + WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12, /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() @@ -142,7 +143,7 @@ int fflush_sync_and_check(FILE *f); int write_timestamp_file_atomic(const char *fn, usec_t n); int read_timestamp_file(const char *fn, usec_t *ret); -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); +int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space); typedef enum ReadLineFlags { READ_LINE_ONLY_NUL = 1 << 0, diff --git a/src/basic/filesystems-gperf.gperf b/src/basic/filesystems-gperf.gperf index 1cd66b5..c82fe98 100644 --- a/src/basic/filesystems-gperf.gperf +++ b/src/basic/filesystems-gperf.gperf @@ -28,6 +28,7 @@ afs, {AFS_FS_MAGIC, AFS_SUPER_MAGIC} anon_inodefs, {ANON_INODE_FS_MAGIC} autofs, {AUTOFS_SUPER_MAGIC} balloon-kvm, {BALLOON_KVM_MAGIC} +bcachefs, {BCACHEFS_SUPER_MAGIC} bdev, {BDEVFS_MAGIC} binder, {BINDERFS_SUPER_MAGIC} binfmt_misc, {BINFMTFS_MAGIC} diff --git a/src/basic/format-util.c b/src/basic/format-util.c index 9450185..445fecc 100644 --- a/src/basic/format-util.c +++ b/src/basic/format-util.c @@ -25,7 +25,7 @@ int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NA } int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) { - char buf[IF_NAMESIZE], *copy; + char buf[IF_NAMESIZE]; int r; assert(ret); @@ -34,12 +34,7 @@ int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) { if (r < 0) return r; - copy = strdup(buf); - if (!copy) - return -ENOMEM; - - *ret = copy; - return 0; + return strdup_to(ret, buf); } char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) { @@ -75,15 +70,17 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) { for (size_t i = 0; i < n; i++) if (t >= table[i].factor) { - if (flag & FORMAT_BYTES_BELOW_POINT) { + uint64_t remainder = i != n - 1 ? + (t / table[i + 1].factor * 10 / table[n - 1].factor) % 10 : + (t * 10 / table[i].factor) % 10; + + if (FLAGS_SET(flag, FORMAT_BYTES_BELOW_POINT) && remainder > 0) (void) snprintf(buf, l, "%" PRIu64 ".%" PRIu64 "%s", t / table[i].factor, - i != n - 1 ? - (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10): - (t * UINT64_C(10) / table[i].factor) % UINT64_C(10), + remainder, table[i].suffix); - } else + else (void) snprintf(buf, l, "%" PRIu64 "%s", t / table[i].factor, diff --git a/src/basic/format-util.h b/src/basic/format-util.h index 8719df3..ba7cff6 100644 --- a/src/basic/format-util.h +++ b/src/basic/format-util.h @@ -18,6 +18,14 @@ assert_cc(sizeof(uid_t) == sizeof(uint32_t)); assert_cc(sizeof(gid_t) == sizeof(uint32_t)); #define GID_FMT "%" PRIu32 +/* Note: the lifetime of the compound literal is the immediately surrounding block, + * see C11 §6.5.2.5, and + * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */ +#define FORMAT_UID(uid) \ + snprintf_ok((char[DECIMAL_STR_MAX(uid_t)]){}, DECIMAL_STR_MAX(uid_t), UID_FMT, uid) +#define FORMAT_GID(gid) \ + snprintf_ok((char[DECIMAL_STR_MAX(gid_t)]){}, DECIMAL_STR_MAX(gid_t), GID_FMT, gid) + #if SIZEOF_TIME_T == 8 # define PRI_TIME PRIi64 #elif SIZEOF_TIME_T == 4 diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 5bc7d2f..64d3093 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -325,12 +325,22 @@ int fchmod_opath(int fd, mode_t m) { int futimens_opath(int fd, const struct timespec ts[2]) { /* Similar to fchmod_opath() but for futimens() */ - if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) { + assert(fd >= 0); + + if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0) + return 0; + if (errno != EINVAL) + return -errno; + + /* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/ + * if unavailable. */ + + if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, /* flags = */ 0) < 0) { if (errno != ENOENT) return -errno; if (proc_mounted() == 0) - return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + return -ENOSYS; return -ENOENT; } @@ -405,17 +415,14 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi ret = fchmod_and_chown(fd, mode, uid, gid); if (stamp != USEC_INFINITY) { - struct timespec ts[2]; + struct timespec ts; + timespec_store(&ts, stamp); - timespec_store(&ts[0], stamp); - ts[1] = ts[0]; - r = futimens_opath(fd, ts); + r = futimens_opath(fd, (const struct timespec[2]) { ts, ts }); } else - r = futimens_opath(fd, NULL); - if (r < 0 && ret >= 0) - return r; + r = futimens_opath(fd, /* ts = */ NULL); - return ret; + return RET_GATHER(ret, r); } int symlink_idempotent(const char *from, const char *to, bool make_relative) { @@ -1018,7 +1025,7 @@ int parse_cifs_service( return 0; } -int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { +int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode) { _cleanup_close_ int fd = -EBADF, parent_fd = -EBADF; _cleanup_free_ char *fname = NULL, *parent = NULL; int r; @@ -1054,7 +1061,7 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { path = fname; } - fd = xopenat_full(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, mode); if (IN_SET(fd, -ELOOP, -ENOTDIR)) return -EEXIST; if (fd < 0) @@ -1236,3 +1243,99 @@ int xopenat_lock_full( return TAKE_FD(fd); } + +int link_fd(int fd, int newdirfd, const char *newpath) { + int r; + + assert(fd >= 0); + assert(newdirfd >= 0 || newdirfd == AT_FDCWD); + assert(newpath); + + /* Try linking via /proc/self/fd/ first. */ + r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW)); + if (r != -ENOENT) + return r; + + /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a + * more recent kernel, but does not require /proc/ mounted) */ + if (proc_mounted() != 0) + return r; + + return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH)); +} + +int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { + _cleanup_close_ int old_fd = -EBADF; + int r; + + assert(olddirfd >= 0 || olddirfd == AT_FDCWD); + assert(newdirfd >= 0 || newdirfd == AT_FDCWD); + assert(!isempty(newpath)); /* source path is optional, but the target path is not */ + + /* Like linkat() but replaces the target if needed. Is a NOP if source and target already share the + * same inode. */ + + if (olddirfd == AT_FDCWD && isempty(oldpath)) /* Refuse operating on the cwd (which is a dir, and dirs can't be hardlinked) */ + return -EISDIR; + + if (path_implies_directory(oldpath)) /* Refuse these definite directories early */ + return -EISDIR; + + if (path_implies_directory(newpath)) + return -EISDIR; + + /* First, try to link this directly */ + if (oldpath) + r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, newpath, 0)); + else + r = link_fd(olddirfd, newdirfd, newpath); + if (r >= 0) + return 0; + if (r != -EEXIST) + return r; + + old_fd = xopenat(olddirfd, oldpath, O_PATH|O_CLOEXEC); + if (old_fd < 0) + return old_fd; + + struct stat old_st; + if (fstat(old_fd, &old_st) < 0) + return -errno; + + if (S_ISDIR(old_st.st_mode)) /* Don't bother if we are operating on a directory */ + return -EISDIR; + + struct stat new_st; + if (fstatat(newdirfd, newpath, &new_st, AT_SYMLINK_NOFOLLOW) < 0) + return -errno; + + if (S_ISDIR(new_st.st_mode)) /* Refuse replacing directories */ + return -EEXIST; + + if (stat_inode_same(&old_st, &new_st)) /* Already the same inode? Then shortcut this */ + return 0; + + _cleanup_free_ char *tmp_path = NULL; + r = tempfn_random(newpath, /* extra= */ NULL, &tmp_path); + if (r < 0) + return r; + + r = link_fd(old_fd, newdirfd, tmp_path); + if (r < 0) { + if (!ERRNO_IS_PRIVILEGE(r)) + return r; + + /* If that didn't work due to permissions then go via the path of the dentry */ + r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, tmp_path, 0)); + if (r < 0) + return r; + } + + r = RET_NERRNO(renameat(newdirfd, tmp_path, newdirfd, newpath)); + if (r < 0) { + (void) unlinkat(newdirfd, tmp_path, /* flags= */ 0); + return r; + } + + return 0; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 6a1e2e7..3e2db95 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -128,15 +128,18 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path); -int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode); - -int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created); - typedef enum XOpenFlags { XO_LABEL = 1 << 0, XO_SUBVOLUME = 1 << 1, } XOpenFlags; +int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode); +static inline int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { + return open_mkdir_at_full(dirfd, path, flags, 0, mode); +} + +int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created); + 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); @@ -146,3 +149,7 @@ int xopenat_lock_full(int dir_fd, const char *path, int open_flags, XOpenFlags x 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); } + +int link_fd(int fd, int newdirfd, const char *newpath); + +int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); diff --git a/src/basic/gcrypt-util.c b/src/basic/gcrypt-util.c index 41c9362..4d68d2c 100644 --- a/src/basic/gcrypt-util.c +++ b/src/basic/gcrypt-util.c @@ -5,41 +5,130 @@ #include "gcrypt-util.h" #include "hexdecoct.h" -void initialize_libgcrypt(bool secmem) { - if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) - return; +static void *gcrypt_dl = NULL; - gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM); - assert_se(gcry_check_version("1.4.5")); +static DLSYM_FUNCTION(gcry_control); +static DLSYM_FUNCTION(gcry_check_version); +DLSYM_FUNCTION(gcry_md_close); +DLSYM_FUNCTION(gcry_md_copy); +DLSYM_FUNCTION(gcry_md_ctl); +DLSYM_FUNCTION(gcry_md_get_algo_dlen); +DLSYM_FUNCTION(gcry_md_open); +DLSYM_FUNCTION(gcry_md_read); +DLSYM_FUNCTION(gcry_md_reset); +DLSYM_FUNCTION(gcry_md_setkey); +DLSYM_FUNCTION(gcry_md_write); +DLSYM_FUNCTION(gcry_mpi_add); +DLSYM_FUNCTION(gcry_mpi_add_ui); +DLSYM_FUNCTION(gcry_mpi_cmp); +DLSYM_FUNCTION(gcry_mpi_cmp_ui); +DLSYM_FUNCTION(gcry_mpi_get_nbits); +DLSYM_FUNCTION(gcry_mpi_invm); +DLSYM_FUNCTION(gcry_mpi_mod); +DLSYM_FUNCTION(gcry_mpi_mul); +DLSYM_FUNCTION(gcry_mpi_mulm); +DLSYM_FUNCTION(gcry_mpi_new); +DLSYM_FUNCTION(gcry_mpi_powm); +DLSYM_FUNCTION(gcry_mpi_print); +DLSYM_FUNCTION(gcry_mpi_release); +DLSYM_FUNCTION(gcry_mpi_scan); +DLSYM_FUNCTION(gcry_mpi_set_ui); +DLSYM_FUNCTION(gcry_mpi_sub); +DLSYM_FUNCTION(gcry_mpi_subm); +DLSYM_FUNCTION(gcry_mpi_sub_ui); +DLSYM_FUNCTION(gcry_prime_check); +DLSYM_FUNCTION(gcry_randomize); +DLSYM_FUNCTION(gcry_strerror); + +static int dlopen_gcrypt(void) { + ELF_NOTE_DLOPEN("gcrypt", + "Support for journald forward-sealing", + ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + "libgcrypt.so.20"); + + return dlopen_many_sym_or_warn( + &gcrypt_dl, + "libgcrypt.so.20", LOG_DEBUG, + DLSYM_ARG(gcry_control), + DLSYM_ARG(gcry_check_version), + DLSYM_ARG(gcry_md_close), + DLSYM_ARG(gcry_md_copy), + DLSYM_ARG(gcry_md_ctl), + DLSYM_ARG(gcry_md_get_algo_dlen), + DLSYM_ARG(gcry_md_open), + DLSYM_ARG(gcry_md_read), + DLSYM_ARG(gcry_md_reset), + DLSYM_ARG(gcry_md_setkey), + DLSYM_ARG(gcry_md_write), + DLSYM_ARG(gcry_mpi_add), + DLSYM_ARG(gcry_mpi_add_ui), + DLSYM_ARG(gcry_mpi_cmp), + DLSYM_ARG(gcry_mpi_cmp_ui), + DLSYM_ARG(gcry_mpi_get_nbits), + DLSYM_ARG(gcry_mpi_invm), + DLSYM_ARG(gcry_mpi_mod), + DLSYM_ARG(gcry_mpi_mul), + DLSYM_ARG(gcry_mpi_mulm), + DLSYM_ARG(gcry_mpi_new), + DLSYM_ARG(gcry_mpi_powm), + DLSYM_ARG(gcry_mpi_print), + DLSYM_ARG(gcry_mpi_release), + DLSYM_ARG(gcry_mpi_scan), + DLSYM_ARG(gcry_mpi_set_ui), + DLSYM_ARG(gcry_mpi_sub), + DLSYM_ARG(gcry_mpi_subm), + DLSYM_ARG(gcry_mpi_sub_ui), + DLSYM_ARG(gcry_prime_check), + DLSYM_ARG(gcry_randomize), + DLSYM_ARG(gcry_strerror)); +} + +int initialize_libgcrypt(bool secmem) { + int r; + + r = dlopen_gcrypt(); + if (r < 0) + return r; + + if (sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) + return 0; + + sym_gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM); + assert_se(sym_gcry_check_version("1.4.5")); /* Turn off "secmem". Clients which wish to make use of this * feature should initialize the library manually */ if (!secmem) - gcry_control(GCRYCTL_DISABLE_SECMEM); + sym_gcry_control(GCRYCTL_DISABLE_SECMEM); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + + return 0; } # if !PREFER_OPENSSL int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) { - _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; + _cleanup_(sym_gcry_md_closep) gcry_md_hd_t md = NULL; gcry_error_t err; size_t hash_size; void *hash; char *enc; + int r; - initialize_libgcrypt(false); + r = initialize_libgcrypt(false); + if (r < 0) + return r; - hash_size = gcry_md_get_algo_dlen(md_algorithm); + hash_size = sym_gcry_md_get_algo_dlen(md_algorithm); assert(hash_size > 0); - err = gcry_md_open(&md, md_algorithm, 0); + err = sym_gcry_md_open(&md, md_algorithm, 0); if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md) return -EIO; - gcry_md_write(md, s, len); + sym_gcry_md_write(md, s, len); - hash = gcry_md_read(md, 0); + hash = sym_gcry_md_read(md, 0); if (!hash) return -EIO; diff --git a/src/basic/gcrypt-util.h b/src/basic/gcrypt-util.h index 4c40cef..acb50e8 100644 --- a/src/basic/gcrypt-util.h +++ b/src/basic/gcrypt-util.h @@ -9,11 +9,59 @@ #if HAVE_GCRYPT #include <gcrypt.h> +#include "dlfcn-util.h" #include "macro.h" -void initialize_libgcrypt(bool secmem); +DLSYM_PROTOTYPE(gcry_md_close); +DLSYM_PROTOTYPE(gcry_md_copy); +DLSYM_PROTOTYPE(gcry_md_ctl); +DLSYM_PROTOTYPE(gcry_md_get_algo_dlen); +DLSYM_PROTOTYPE(gcry_md_open); +DLSYM_PROTOTYPE(gcry_md_read); +DLSYM_PROTOTYPE(gcry_md_reset); +DLSYM_PROTOTYPE(gcry_md_setkey); +DLSYM_PROTOTYPE(gcry_md_write); +DLSYM_PROTOTYPE(gcry_mpi_add); +DLSYM_PROTOTYPE(gcry_mpi_add_ui); +DLSYM_PROTOTYPE(gcry_mpi_cmp); +DLSYM_PROTOTYPE(gcry_mpi_cmp_ui); +DLSYM_PROTOTYPE(gcry_mpi_get_nbits); +DLSYM_PROTOTYPE(gcry_mpi_invm); +DLSYM_PROTOTYPE(gcry_mpi_mod); +DLSYM_PROTOTYPE(gcry_mpi_mul); +DLSYM_PROTOTYPE(gcry_mpi_mulm); +DLSYM_PROTOTYPE(gcry_mpi_new); +DLSYM_PROTOTYPE(gcry_mpi_powm); +DLSYM_PROTOTYPE(gcry_mpi_print); +DLSYM_PROTOTYPE(gcry_mpi_release); +DLSYM_PROTOTYPE(gcry_mpi_scan); +DLSYM_PROTOTYPE(gcry_mpi_set_ui); +DLSYM_PROTOTYPE(gcry_mpi_sub); +DLSYM_PROTOTYPE(gcry_mpi_subm); +DLSYM_PROTOTYPE(gcry_mpi_sub_ui); +DLSYM_PROTOTYPE(gcry_prime_check); +DLSYM_PROTOTYPE(gcry_randomize); +DLSYM_PROTOTYPE(gcry_strerror); +int initialize_libgcrypt(bool secmem); + +static inline gcry_md_hd_t* sym_gcry_md_closep(gcry_md_hd_t *md) { + if (!md || !*md) + return NULL; + sym_gcry_md_close(*md); + + return NULL; +} DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, gcry_md_close, NULL); + +/* Copied from gcry_md_putc from gcrypt.h due to the need to call the sym_ variant */ +#define sym_gcry_md_putc(h,c) \ + do { \ + gcry_md_hd_t h__ = (h); \ + if ((h__)->bufpos == (h__)->bufsize) \ + sym_gcry_md_write((h__), NULL, 0); \ + (h__)->buf[(h__)->bufpos++] = (c) & 0xff; \ + } while(false) #endif #if !PREFER_OPENSSL diff --git a/src/basic/getopt-defs.h b/src/basic/getopt-defs.h index 3efeb6d..9abef6f 100644 --- a/src/basic/getopt-defs.h +++ b/src/basic/getopt-defs.h @@ -26,6 +26,7 @@ ARG_CRASH_CHVT, \ ARG_CRASH_SHELL, \ ARG_CRASH_REBOOT, \ + ARG_CRASH_ACTION, \ ARG_CONFIRM_SPAWN, \ ARG_SHOW_STATUS, \ ARG_DESERIALIZE, \ @@ -61,6 +62,7 @@ { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, \ { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, \ { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT }, \ + { "crash-action", required_argument, NULL, ARG_CRASH_ACTION }, \ { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, \ { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, \ { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, \ diff --git a/src/basic/glyph-util.c b/src/basic/glyph-util.c index 803bdd9..d37be32 100644 --- a/src/basic/glyph-util.c +++ b/src/basic/glyph-util.c @@ -41,6 +41,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) { [SPECIAL_GLYPH_TREE_SPACE] = " ", [SPECIAL_GLYPH_TREE_TOP] = ",-", [SPECIAL_GLYPH_VERTICAL_DOTTED] = ":", + [SPECIAL_GLYPH_HORIZONTAL_DOTTED] = "-", + [SPECIAL_GLYPH_HORIZONTAL_FAT] = "=", [SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">", [SPECIAL_GLYPH_BLACK_CIRCLE] = "*", [SPECIAL_GLYPH_WHITE_CIRCLE] = "*", @@ -74,6 +76,10 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) { [SPECIAL_GLYPH_SPARKLES] = "*", [SPECIAL_GLYPH_LOW_BATTERY] = "!", [SPECIAL_GLYPH_WARNING_SIGN] = "!", + [SPECIAL_GLYPH_RED_CIRCLE] = "o", + [SPECIAL_GLYPH_YELLOW_CIRCLE] = "o", + [SPECIAL_GLYPH_BLUE_CIRCLE] = "o", + [SPECIAL_GLYPH_GREEN_CIRCLE] = "o", }, /* UTF-8 */ @@ -87,6 +93,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) { /* Single glyphs in both cases */ [SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"┆", + [SPECIAL_GLYPH_HORIZONTAL_DOTTED] = u8"┄", + [SPECIAL_GLYPH_HORIZONTAL_FAT] = u8"━", [SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"‣", [SPECIAL_GLYPH_BLACK_CIRCLE] = u8"●", [SPECIAL_GLYPH_WHITE_CIRCLE] = u8"○", @@ -136,6 +144,11 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) { [SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️", [SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽", [SPECIAL_GLYPH_WORLD] = u8"🌍", + + [SPECIAL_GLYPH_RED_CIRCLE] = u8"🔴", + [SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡", + [SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵", + [SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢", }, }; diff --git a/src/basic/glyph-util.h b/src/basic/glyph-util.h index a770997..db8dbbf 100644 --- a/src/basic/glyph-util.h +++ b/src/basic/glyph-util.h @@ -13,6 +13,8 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_TREE_SPACE, SPECIAL_GLYPH_TREE_TOP, SPECIAL_GLYPH_VERTICAL_DOTTED, + SPECIAL_GLYPH_HORIZONTAL_DOTTED, + SPECIAL_GLYPH_HORIZONTAL_FAT, SPECIAL_GLYPH_TRIANGULAR_BULLET, SPECIAL_GLYPH_BLACK_CIRCLE, SPECIAL_GLYPH_WHITE_CIRCLE, @@ -49,6 +51,10 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_WARNING_SIGN, SPECIAL_GLYPH_COMPUTER_DISK, SPECIAL_GLYPH_WORLD, + SPECIAL_GLYPH_RED_CIRCLE, + SPECIAL_GLYPH_YELLOW_CIRCLE, + SPECIAL_GLYPH_BLUE_CIRCLE, + SPECIAL_GLYPH_GREEN_CIRCLE, _SPECIAL_GLYPH_MAX, _SPECIAL_GLYPH_INVALID = -EINVAL, } SpecialGlyph; diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c index 5fac467..251ee4f 100644 --- a/src/basic/hash-funcs.c +++ b/src/basic/hash-funcs.c @@ -33,7 +33,7 @@ void path_hash_func(const char *q, struct siphash *state) { /* if path is absolute, add one "/" to the hash. */ if (path_is_absolute(q)) - siphash24_compress("/", 1, state); + siphash24_compress_byte('/', state); for (;;) { const char *e; @@ -67,7 +67,7 @@ DEFINE_HASH_OPS_FULL(path_hash_ops_free_free, void, free); void trivial_hash_func(const void *p, struct siphash *state) { - siphash24_compress(&p, sizeof(p), state); + siphash24_compress_typesafe(p, state); } int trivial_compare_func(const void *a, const void *b) { @@ -93,7 +93,7 @@ const struct hash_ops trivial_hash_ops_free_free = { }; void uint64_hash_func(const uint64_t *p, struct siphash *state) { - siphash24_compress(p, sizeof(uint64_t), state); + siphash24_compress_typesafe(*p, state); } int uint64_compare_func(const uint64_t *a, const uint64_t *b) { @@ -104,7 +104,7 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func #if SIZEOF_DEV_T != 8 void devt_hash_func(const dev_t *p, struct siphash *state) { - siphash24_compress(p, sizeof(dev_t), state); + siphash24_compress_typesafe(*p, state); } #endif diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 894760c..a9fd762 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -2120,24 +2120,27 @@ static int hashmap_entry_compare( return compare((*a)->key, (*b)->key); } -int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) { - _cleanup_free_ struct hashmap_base_entry **entries = NULL; +static int _hashmap_dump_entries_sorted( + HashmapBase *h, + void ***ret, + size_t *ret_n) { + _cleanup_free_ void **entries = NULL; Iterator iter; unsigned idx; size_t n = 0; assert(ret); + assert(ret_n); if (_hashmap_size(h) == 0) { *ret = NULL; - if (ret_n) - *ret_n = 0; + *ret_n = 0; return 0; } /* We append one more element than needed so that the resulting array can be used as a strv. We * don't count this entry in the returned size. */ - entries = new(struct hashmap_base_entry*, _hashmap_size(h) + 1); + entries = new(void*, _hashmap_size(h) + 1); if (!entries) return -ENOMEM; @@ -2147,13 +2150,47 @@ int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) { assert(n == _hashmap_size(h)); entries[n] = NULL; - typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare); + typesafe_qsort_r((struct hashmap_base_entry**) entries, n, + hashmap_entry_compare, h->hash_ops->compare); + + *ret = TAKE_PTR(entries); + *ret_n = n; + return 0; +} + +int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n) { + _cleanup_free_ void **entries = NULL; + size_t n; + int r; + + r = _hashmap_dump_entries_sorted(h, &entries, &n); + if (r < 0) + return r; + + /* Reuse the array. */ + FOREACH_ARRAY(e, entries, n) + *e = (void*) (*(struct hashmap_base_entry**) e)->key; + + *ret = TAKE_PTR(entries); + if (ret_n) + *ret_n = n; + return 0; +} + +int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) { + _cleanup_free_ void **entries = NULL; + size_t n; + int r; + + r = _hashmap_dump_entries_sorted(h, &entries, &n); + if (r < 0) + return r; /* Reuse the array. */ FOREACH_ARRAY(e, entries, n) - *e = entry_value(h, *e); + *e = entry_value(h, *(struct hashmap_base_entry**) e); - *ret = (void**) TAKE_PTR(entries); + *ret = TAKE_PTR(entries); if (ret_n) *ret_n = n; return 0; diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index d0ebdf5..49d9d11 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -409,6 +409,14 @@ static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) { return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n); } +int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n); +static inline int hashmap_dump_keys_sorted(Hashmap *h, void ***ret, size_t *ret_n) { + return _hashmap_dump_keys_sorted(HASHMAP_BASE(h), ret, ret_n); +} +static inline int ordered_hashmap_dump_keys_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) { + return _hashmap_dump_keys_sorted(HASHMAP_BASE(h), ret, ret_n); +} + /* * Hashmaps are iterated in unpredictable order. * OrderedHashmaps are an exception to this. They are iterated in the order diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index ea683eb..4cb67d9 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -114,7 +114,7 @@ int unhexmem_full( const char *p, size_t l, bool secure, - void **ret, + void **ret_data, size_t *ret_len) { _cleanup_free_ uint8_t *buf = NULL; @@ -155,8 +155,8 @@ int unhexmem_full( if (ret_len) *ret_len = (size_t) (z - buf); - if (ret) - *ret = TAKE_PTR(buf); + if (ret_data) + *ret_data = TAKE_PTR(buf); return 0; } @@ -766,7 +766,7 @@ int unbase64mem_full( const char *p, size_t l, bool secure, - void **ret, + void **ret_data, size_t *ret_size) { _cleanup_free_ uint8_t *buf = NULL; @@ -854,8 +854,8 @@ int unbase64mem_full( if (ret_size) *ret_size = (size_t) (z - buf); - if (ret) - *ret = TAKE_PTR(buf); + if (ret_data) + *ret_data = TAKE_PTR(buf); return 0; } diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h index 319b21a..0a10af3 100644 --- a/src/basic/hexdecoct.h +++ b/src/basic/hexdecoct.h @@ -18,9 +18,9 @@ char hexchar(int x) _const_; int unhexchar(char c) _const_; char *hexmem(const void *p, size_t l); -int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); -static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) { - return unhexmem_full(p, l, false, mem, len); +int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size); +static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) { + return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size); } char base32hexchar(int x) _const_; @@ -45,9 +45,9 @@ ssize_t base64_append( size_t l, size_t margin, size_t width); -int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); -static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) { - return unbase64mem_full(p, l, false, mem, len); +int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size); +static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) { + return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size); } void hexdump(FILE *f, const void *p, size_t s); diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index ee4ea67..c077f0a 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -91,14 +91,26 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) { be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001); } +bool in4_addr_is_multicast(const struct in_addr *a) { + assert(a); + + return IN_MULTICAST(be32toh(a->s_addr)); +} + +bool in6_addr_is_multicast(const struct in6_addr *a) { + assert(a); + + return IN6_IS_ADDR_MULTICAST(a); +} + int in_addr_is_multicast(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) - return IN_MULTICAST(be32toh(u->in.s_addr)); + return in4_addr_is_multicast(&u->in); if (family == AF_INET6) - return IN6_IS_ADDR_MULTICAST(&u->in6); + return in6_addr_is_multicast(&u->in6); return -EAFNOSUPPORT; } @@ -182,58 +194,69 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_ return -EAFNOSUPPORT; } -int in_addr_prefix_intersect( - int family, - const union in_addr_union *a, +bool in4_addr_prefix_intersect( + const struct in_addr *a, unsigned aprefixlen, - const union in_addr_union *b, + const struct in_addr *b, unsigned bprefixlen) { - unsigned m; - assert(a); assert(b); - /* Checks whether there are any addresses that are in both networks */ + unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in_addr) * 8)); + if (m == 0) + return true; /* Let's return earlier, to avoid shift by 32. */ - m = MIN(aprefixlen, bprefixlen); + uint32_t x = be32toh(a->s_addr ^ b->s_addr); + uint32_t n = 0xFFFFFFFFUL << (32 - m); + return (x & n) == 0; +} - if (family == AF_INET) { - uint32_t x, nm; +bool in6_addr_prefix_intersect( + const struct in6_addr *a, + unsigned aprefixlen, + const struct in6_addr *b, + unsigned bprefixlen) { - x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = m == 0 ? 0 : 0xFFFFFFFFUL << (32 - m); + assert(a); + assert(b); - return (x & nm) == 0; - } + unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in6_addr) * 8)); + if (m == 0) + return true; - if (family == AF_INET6) { - unsigned i; + for (size_t i = 0; i < sizeof(struct in6_addr); i++) { + uint8_t x = a->s6_addr[i] ^ b->s6_addr[i]; + uint8_t n = m < 8 ? (0xFF << (8 - m)) : 0xFF; + if ((x & n) != 0) + return false; - if (m > 128) - m = 128; + if (m <= 8) + break; - for (i = 0; i < 16; i++) { - uint8_t x, nm; + m -= 8; + } - x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; + return true; +} - if (m < 8) - nm = 0xFF << (8 - m); - else - nm = 0xFF; +int in_addr_prefix_intersect( + int family, + const union in_addr_union *a, + unsigned aprefixlen, + const union in_addr_union *b, + unsigned bprefixlen) { - if ((x & nm) != 0) - return 0; + assert(a); + assert(b); - if (m > 8) - m -= 8; - else - m = 0; - } + /* Checks whether there are any addresses that are in both networks. */ - return 1; - } + if (family == AF_INET) + return in4_addr_prefix_intersect(&a->in, aprefixlen, &b->in, bprefixlen); + + if (family == AF_INET6) + return in6_addr_prefix_intersect(&a->in6, aprefixlen, &b->in6, bprefixlen); return -EAFNOSUPPORT; } @@ -922,12 +945,19 @@ int in_addr_prefix_from_string_auto_internal( } +void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) { + assert(u); + assert(state); + + siphash24_compress(u->bytes, FAMILY_ADDRESS_SIZE(family), state); +} + void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) { assert(a); assert(state); - siphash24_compress(&a->family, sizeof(a->family), state); - siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); + siphash24_compress_typesafe(a->family, state); + in_addr_hash_func(&a->address, a->family, state); } int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) { @@ -960,7 +990,7 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) { assert(addr); assert(state); - siphash24_compress(addr, sizeof(*addr), state); + siphash24_compress_typesafe(*addr, state); } int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) { diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 12720ca..9cd0aca 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -40,6 +40,8 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) { return in_addr_data_is_null(a); } +bool in4_addr_is_multicast(const struct in_addr *a); +bool in6_addr_is_multicast(const struct in6_addr *a); int in_addr_is_multicast(int family, const union in_addr_union *u); bool in4_addr_is_link_local(const struct in_addr *a); @@ -59,7 +61,22 @@ bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a); bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); -int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); +bool in4_addr_prefix_intersect( + const struct in_addr *a, + unsigned aprefixlen, + const struct in_addr *b, + unsigned bprefixlen); +bool in6_addr_prefix_intersect( + const struct in6_addr *a, + unsigned aprefixlen, + const struct in6_addr *b, + unsigned bprefixlen); +int in_addr_prefix_intersect( + int family, + const union in_addr_union *a, + unsigned aprefixlen, + const union in_addr_union *b, + unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth); int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); @@ -185,6 +202,7 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) { * See also oss-fuzz#11344. */ #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} }) +void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state); void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state); int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y); void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state); diff --git a/src/basic/initrd-util.c b/src/basic/initrd-util.c index 03ccfbe..d3aa933 100644 --- a/src/basic/initrd-util.c +++ b/src/basic/initrd-util.c @@ -21,7 +21,7 @@ bool in_initrd(void) { * This can be overridden by setting SYSTEMD_IN_INITRD=0|1. */ - r = getenv_bool_secure("SYSTEMD_IN_INITRD"); + r = secure_getenv_bool("SYSTEMD_IN_INITRD"); if (r < 0 && r != -ENXIO) log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); diff --git a/src/basic/iovec-util.c b/src/basic/iovec-util.c index 991889a..6456945 100644 --- a/src/basic/iovec-util.c +++ b/src/basic/iovec-util.c @@ -62,8 +62,10 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch return x; } -void iovec_array_free(struct iovec *iovec, size_t n) { - FOREACH_ARRAY(i, iovec, n) +void iovec_array_free(struct iovec *iovec, size_t n_iovec) { + assert(iovec || n_iovec == 0); + + FOREACH_ARRAY(i, iovec, n_iovec) free(i->iov_base); free(iovec); diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h index 39feabd..8cfa571 100644 --- a/src/basic/iovec-util.h +++ b/src/basic/iovec-util.h @@ -8,16 +8,38 @@ #include "alloc-util.h" #include "macro.h" +/* An iovec pointing to a single NUL byte */ +#define IOVEC_NUL_BYTE (const struct iovec) { \ + .iov_base = (void*) (const uint8_t[1]) { 0 }, \ + .iov_len = 1, \ + } + size_t iovec_total_size(const struct iovec *iovec, size_t n); bool iovec_increment(struct iovec *iovec, size_t n, size_t k); -#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) } -#define IOVEC_MAKE_STRING(string) \ - ({ \ - const char *_s = (string); \ - IOVEC_MAKE((char*) _s, strlen(_s)); \ - }) +/* This accepts both const and non-const pointers */ +#define IOVEC_MAKE(base, len) \ + (struct iovec) { \ + .iov_base = (void*) (base), \ + .iov_len = (len), \ + } + +static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) { + assert(iovec); + /* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */ + *iovec = IOVEC_MAKE(s, s ? strlen(s) : 0); + return iovec; +} + +#define IOVEC_MAKE_STRING(s) \ + *iovec_make_string(&(struct iovec) {}, s) + +#define CONST_IOVEC_MAKE_STRING(s) \ + (const struct iovec) { \ + .iov_base = (char*) s, \ + .iov_len = STRLEN(s), \ + } static inline void iovec_done(struct iovec *iovec) { /* A _cleanup_() helper that frees the iov_base in the iovec */ @@ -35,10 +57,43 @@ static inline void iovec_done_erase(struct iovec *iovec) { } static inline bool iovec_is_set(const struct iovec *iovec) { + /* Checks if the iovec points to a non-empty chunk of memory */ return iovec && iovec->iov_len > 0 && iovec->iov_base; } +static inline bool iovec_is_valid(const struct iovec *iovec) { + /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */ + return !iovec || (iovec->iov_base || iovec->iov_len == 0); +} + char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value); -void iovec_array_free(struct iovec *iovec, size_t n); +void iovec_array_free(struct iovec *iovec, size_t n_iovec); + +static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) { + + if (a == b) + return 0; + + return memcmp_nn(a ? a->iov_base : NULL, + a ? a->iov_len : 0, + b ? b->iov_base : NULL, + b ? b->iov_len : 0); +} + +static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) { + assert(ret); + + if (!iovec_is_set(source)) + *ret = (struct iovec) {}; + else { + void *p = memdup(source->iov_base, source->iov_len); + if (!p) + return NULL; + + *ret = IOVEC_MAKE(p, source->iov_len); + } + + return ret; +} diff --git a/src/basic/keyring-util.c b/src/basic/keyring-util.c new file mode 100644 index 0000000..c32bd50 --- /dev/null +++ b/src/basic/keyring-util.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "keyring-util.h" +#include "memory-util.h" +#include "missing_syscall.h" + +int keyring_read(key_serial_t serial, void **ret, size_t *ret_size) { + size_t bufsize = 100; + + for (;;) { + _cleanup_(erase_and_freep) uint8_t *buf = NULL; + long n; + + buf = new(uint8_t, bufsize + 1); + if (!buf) + return -ENOMEM; + + n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) buf, (unsigned long) bufsize, 0); + if (n < 0) + return -errno; + + if ((size_t) n <= bufsize) { + buf[n] = 0; /* NUL terminate, just in case */ + + if (ret) + *ret = TAKE_PTR(buf); + if (ret_size) + *ret_size = n; + + return 0; + } + + bufsize = (size_t) n; + } +} + +int keyring_describe(key_serial_t serial, char **ret) { + _cleanup_free_ char *tuple = NULL; + size_t sz = 64; + int c = -1; /* Workaround for maybe-uninitialized false positive due to missing_syscall indirection */ + + assert(ret); + + for (;;) { + tuple = new(char, sz); + if (!tuple) + return log_oom_debug(); + + c = keyctl(KEYCTL_DESCRIBE, serial, (unsigned long) tuple, c, 0); + if (c < 0) + return log_debug_errno(errno, "Failed to describe key id %d: %m", serial); + + if ((size_t) c <= sz) + break; + + sz = c; + free(tuple); + } + + /* The kernel returns a final NUL in the string, verify that. */ + assert(tuple[c-1] == 0); + + *ret = TAKE_PTR(tuple); + + return 0; +} diff --git a/src/basic/keyring-util.h b/src/basic/keyring-util.h new file mode 100644 index 0000000..6e6e685 --- /dev/null +++ b/src/basic/keyring-util.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <sys/types.h> + +#include "missing_keyctl.h" + +/* Like TAKE_PTR() but for key_serial_t, resetting them to -1 */ +#define TAKE_KEY_SERIAL(key_serial) TAKE_GENERIC(key_serial, key_serial_t, -1) + +int keyring_read(key_serial_t serial, void **ret, size_t *ret_size); +int keyring_describe(key_serial_t serial, char **ret); diff --git a/src/basic/label.c b/src/basic/label.c index f134e77..8b084a7 100644 --- a/src/basic/label.c +++ b/src/basic/label.c @@ -28,3 +28,7 @@ int label_ops_post(int dir_fd, const char *path) { return label_ops->post(dir_fd, path); } + +void label_ops_reset(void) { + label_ops = NULL; +} diff --git a/src/basic/label.h b/src/basic/label.h index 9644e43..a070bf2 100644 --- a/src/basic/label.h +++ b/src/basic/label.h @@ -12,3 +12,4 @@ int label_ops_set(const LabelOps *label_ops); int label_ops_pre(int dir_fd, const char *path, mode_t mode); int label_ops_post(int dir_fd, const char *path); +void label_ops_reset(void); diff --git a/src/basic/linux/btrfs.h b/src/basic/linux/btrfs.h index 74ed908..73a295e 100644 --- a/src/basic/linux/btrfs.h +++ b/src/basic/linux/btrfs.h @@ -94,6 +94,7 @@ struct btrfs_qgroup_limit { * struct btrfs_qgroup_inherit.flags */ #define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) +#define BTRFS_QGROUP_INHERIT_FLAGS_SUPP (BTRFS_QGROUP_INHERIT_SET_LIMITS) struct btrfs_qgroup_inherit { __u64 flags; @@ -189,6 +190,7 @@ struct btrfs_scrub_progress { }; #define BTRFS_SCRUB_READONLY 1 +#define BTRFS_SCRUB_SUPPORTED_FLAGS (BTRFS_SCRUB_READONLY) struct btrfs_ioctl_scrub_args { __u64 devid; /* in */ __u64 start; /* in */ @@ -247,7 +249,17 @@ struct btrfs_ioctl_dev_info_args { __u8 uuid[BTRFS_UUID_SIZE]; /* in/out */ __u64 bytes_used; /* out */ __u64 total_bytes; /* out */ - __u64 unused[379]; /* pad to 4k */ + /* + * Optional, out. + * + * Showing the fsid of the device, allowing user space to check if this + * device is a seeding one. + * + * Introduced in v6.3, thus user space still needs to check if kernel + * changed this value. Older kernel will not touch the values here. + */ + __u8 fsid[BTRFS_UUID_SIZE]; + __u64 unused[377]; /* pad to 4k */ __u8 path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */ }; @@ -324,6 +336,8 @@ struct btrfs_ioctl_fs_info_args { #define BTRFS_FEATURE_INCOMPAT_RAID1C34 (1ULL << 11) #define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12) #define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13) +#define BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE (1ULL << 14) +#define BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA (1ULL << 16) struct btrfs_ioctl_feature_flags { __u64 compat_flags; @@ -603,6 +617,9 @@ struct btrfs_ioctl_clone_range_args { */ #define BTRFS_DEFRAG_RANGE_COMPRESS 1 #define BTRFS_DEFRAG_RANGE_START_IO 2 +#define BTRFS_DEFRAG_RANGE_FLAGS_SUPP (BTRFS_DEFRAG_RANGE_COMPRESS | \ + BTRFS_DEFRAG_RANGE_START_IO) + struct btrfs_ioctl_defrag_range_args { /* start of the defrag operation */ __u64 start; @@ -744,6 +761,7 @@ struct btrfs_ioctl_get_dev_stats { #define BTRFS_QUOTA_CTL_ENABLE 1 #define BTRFS_QUOTA_CTL_DISABLE 2 #define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3 +#define BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA 4 struct btrfs_ioctl_quota_ctl_args { __u64 cmd; __u64 status; diff --git a/src/basic/linux/btrfs_tree.h b/src/basic/linux/btrfs_tree.h index ab38d0f..d24e8e1 100644 --- a/src/basic/linux/btrfs_tree.h +++ b/src/basic/linux/btrfs_tree.h @@ -73,6 +73,9 @@ /* Holds the block group items for extent tree v2. */ #define BTRFS_BLOCK_GROUP_TREE_OBJECTID 11ULL +/* Tracks RAID stripes in block groups. */ +#define BTRFS_RAID_STRIPE_TREE_OBJECTID 12ULL + /* device stats in the device tree */ #define BTRFS_DEV_STATS_OBJECTID 0ULL @@ -216,11 +219,31 @@ */ #define BTRFS_METADATA_ITEM_KEY 169 +/* + * Special inline ref key which stores the id of the subvolume which originally + * created the extent. This subvolume owns the extent permanently from the + * perspective of simple quotas. Needed to know which subvolume to free quota + * usage from when the extent is deleted. + * + * Stored as an inline ref rather to avoid wasting space on a separate item on + * top of the existing extent item. However, unlike the other inline refs, + * there is one one owner ref per extent rather than one per extent. + * + * Because of this, it goes at the front of the list of inline refs, and thus + * must have a lower type value than any other inline ref type (to satisfy the + * disk format rule that inline refs have non-decreasing type). + */ +#define BTRFS_EXTENT_OWNER_REF_KEY 172 + #define BTRFS_TREE_BLOCK_REF_KEY 176 #define BTRFS_EXTENT_DATA_REF_KEY 178 -#define BTRFS_EXTENT_REF_V0_KEY 180 +/* + * Obsolete key. Defintion removed in 6.6, value may be reused in the future. + * + * #define BTRFS_EXTENT_REF_V0_KEY 180 + */ #define BTRFS_SHARED_BLOCK_REF_KEY 182 @@ -257,6 +280,8 @@ #define BTRFS_DEV_ITEM_KEY 216 #define BTRFS_CHUNK_ITEM_KEY 228 +#define BTRFS_RAID_STRIPE_KEY 230 + /* * Records the overall state of the qgroups. * There's only one instance of this key present, @@ -715,6 +740,30 @@ struct btrfs_free_space_header { __le64 num_bitmaps; } __attribute__ ((__packed__)); +struct btrfs_raid_stride { + /* The id of device this raid extent lives on. */ + __le64 devid; + /* The physical location on disk. */ + __le64 physical; +} __attribute__ ((__packed__)); + +/* The stripe_extent::encoding, 1:1 mapping of enum btrfs_raid_types. */ +#define BTRFS_STRIPE_RAID0 1 +#define BTRFS_STRIPE_RAID1 2 +#define BTRFS_STRIPE_DUP 3 +#define BTRFS_STRIPE_RAID10 4 +#define BTRFS_STRIPE_RAID5 5 +#define BTRFS_STRIPE_RAID6 6 +#define BTRFS_STRIPE_RAID1C3 7 +#define BTRFS_STRIPE_RAID1C4 8 + +struct btrfs_stripe_extent { + __u8 encoding; + __u8 reserved[7]; + /* An array of raid strides this stripe is composed of. */ + struct btrfs_raid_stride strides[]; +} __attribute__ ((__packed__)); + #define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0) #define BTRFS_HEADER_FLAG_RELOC (1ULL << 1) @@ -783,6 +832,10 @@ struct btrfs_shared_data_ref { __le32 count; } __attribute__ ((__packed__)); +struct btrfs_extent_owner_ref { + __le64 root_id; +} __attribute__ ((__packed__)); + struct btrfs_extent_inline_ref { __u8 type; __le64 offset; @@ -1200,9 +1253,17 @@ static inline __u16 btrfs_qgroup_level(__u64 qgroupid) */ #define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1ULL << 2) +/* + * Whether or not this filesystem is using simple quotas. Not exactly the + * incompat bit, because we support using simple quotas, disabling it, then + * going back to full qgroup quotas. + */ +#define BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE (1ULL << 3) + #define BTRFS_QGROUP_STATUS_FLAGS_MASK (BTRFS_QGROUP_STATUS_FLAG_ON | \ BTRFS_QGROUP_STATUS_FLAG_RESCAN | \ - BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT) + BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT | \ + BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE) #define BTRFS_QGROUP_STATUS_VERSION 1 @@ -1224,6 +1285,15 @@ struct btrfs_qgroup_status_item { * of the scan. It contains a logical address */ __le64 rescan; + + /* + * The generation when quotas were last enabled. Used by simple quotas to + * avoid decrementing when freeing an extent that was written before + * enable. + * + * Set only if flags contain BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE. + */ + __le64 enable_gen; } __attribute__ ((__packed__)); struct btrfs_qgroup_info_item { diff --git a/src/basic/linux/fou.h b/src/basic/linux/fou.h index 87c2c9f..b5cd3e7 100644 --- a/src/basic/linux/fou.h +++ b/src/basic/linux/fou.h @@ -1,32 +1,37 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* fou.h - FOU Interface */ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/fou.yaml */ +/* YNL-GEN uapi header */ #ifndef _UAPI_LINUX_FOU_H #define _UAPI_LINUX_FOU_H -/* NETLINK_GENERIC related info - */ #define FOU_GENL_NAME "fou" -#define FOU_GENL_VERSION 0x1 +#define FOU_GENL_VERSION 1 enum { - FOU_ATTR_UNSPEC, - FOU_ATTR_PORT, /* u16 */ - FOU_ATTR_AF, /* u8 */ - FOU_ATTR_IPPROTO, /* u8 */ - FOU_ATTR_TYPE, /* u8 */ - FOU_ATTR_REMCSUM_NOPARTIAL, /* flag */ - FOU_ATTR_LOCAL_V4, /* u32 */ - FOU_ATTR_LOCAL_V6, /* in6_addr */ - FOU_ATTR_PEER_V4, /* u32 */ - FOU_ATTR_PEER_V6, /* in6_addr */ - FOU_ATTR_PEER_PORT, /* u16 */ - FOU_ATTR_IFINDEX, /* s32 */ - - __FOU_ATTR_MAX, + FOU_ENCAP_UNSPEC, + FOU_ENCAP_DIRECT, + FOU_ENCAP_GUE, }; -#define FOU_ATTR_MAX (__FOU_ATTR_MAX - 1) +enum { + FOU_ATTR_UNSPEC, + FOU_ATTR_PORT, + FOU_ATTR_AF, + FOU_ATTR_IPPROTO, + FOU_ATTR_TYPE, + FOU_ATTR_REMCSUM_NOPARTIAL, + FOU_ATTR_LOCAL_V4, + FOU_ATTR_LOCAL_V6, + FOU_ATTR_PEER_V4, + FOU_ATTR_PEER_V6, + FOU_ATTR_PEER_PORT, + FOU_ATTR_IFINDEX, + + __FOU_ATTR_MAX +}; +#define FOU_ATTR_MAX (__FOU_ATTR_MAX - 1) enum { FOU_CMD_UNSPEC, @@ -34,15 +39,8 @@ enum { FOU_CMD_DEL, FOU_CMD_GET, - __FOU_CMD_MAX, + __FOU_CMD_MAX }; - -enum { - FOU_ENCAP_UNSPEC, - FOU_ENCAP_DIRECT, - FOU_ENCAP_GUE, -}; - -#define FOU_CMD_MAX (__FOU_CMD_MAX - 1) +#define FOU_CMD_MAX (__FOU_CMD_MAX - 1) #endif /* _UAPI_LINUX_FOU_H */ diff --git a/src/basic/linux/if_bridge.h b/src/basic/linux/if_bridge.h index d9de241..a5b743a 100644 --- a/src/basic/linux/if_bridge.h +++ b/src/basic/linux/if_bridge.h @@ -523,6 +523,9 @@ enum { BRIDGE_VLANDB_ENTRY_TUNNEL_INFO, BRIDGE_VLANDB_ENTRY_STATS, BRIDGE_VLANDB_ENTRY_MCAST_ROUTER, + BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS, + BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS, + BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS, __BRIDGE_VLANDB_ENTRY_MAX, }; #define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) @@ -631,6 +634,11 @@ enum { MDBA_MDB_EATTR_GROUP_MODE, MDBA_MDB_EATTR_SOURCE, MDBA_MDB_EATTR_RTPROT, + MDBA_MDB_EATTR_DST, + MDBA_MDB_EATTR_DST_PORT, + MDBA_MDB_EATTR_VNI, + MDBA_MDB_EATTR_IFINDEX, + MDBA_MDB_EATTR_SRC_VNI, __MDBA_MDB_EATTR_MAX }; #define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1) @@ -715,6 +723,24 @@ enum { }; #define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1) +/* [MDBA_GET_ENTRY] = { + * struct br_mdb_entry + * [MDBA_GET_ENTRY_ATTRS] = { + * [MDBE_ATTR_SOURCE] + * struct in_addr / struct in6_addr + * [MDBE_ATTR_SRC_VNI] + * u32 + * } + * } + */ +enum { + MDBA_GET_ENTRY_UNSPEC, + MDBA_GET_ENTRY, + MDBA_GET_ENTRY_ATTRS, + __MDBA_GET_ENTRY_MAX, +}; +#define MDBA_GET_ENTRY_MAX (__MDBA_GET_ENTRY_MAX - 1) + /* [MDBA_SET_ENTRY_ATTRS] = { * [MDBE_ATTR_xxx] * ... @@ -726,6 +752,12 @@ enum { MDBE_ATTR_SRC_LIST, MDBE_ATTR_GROUP_MODE, MDBE_ATTR_RTPROT, + MDBE_ATTR_DST, + MDBE_ATTR_DST_PORT, + MDBE_ATTR_VNI, + MDBE_ATTR_IFINDEX, + MDBE_ATTR_SRC_VNI, + MDBE_ATTR_STATE_MASK, __MDBE_ATTR_MAX, }; #define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1) diff --git a/src/basic/linux/if_link.h b/src/basic/linux/if_link.h index 1021a7e..ffa637b 100644 --- a/src/basic/linux/if_link.h +++ b/src/basic/linux/if_link.h @@ -374,6 +374,9 @@ enum { IFLA_DEVLINK_PORT, + IFLA_GSO_IPV4_MAX_SIZE, + IFLA_GRO_IPV4_MAX_SIZE, + IFLA_DPLL_PIN, __IFLA_MAX }; @@ -458,6 +461,286 @@ enum in6_addr_gen_mode { /* Bridge section */ +/** + * DOC: Bridge enum definition + * + * Please *note* that the timer values in the following section are expected + * in clock_t format, which is seconds multiplied by USER_HZ (generally + * defined as 100). + * + * @IFLA_BR_FORWARD_DELAY + * The bridge forwarding delay is the time spent in LISTENING state + * (before moving to LEARNING) and in LEARNING state (before moving + * to FORWARDING). Only relevant if STP is enabled. + * + * The valid values are between (2 * USER_HZ) and (30 * USER_HZ). + * The default value is (15 * USER_HZ). + * + * @IFLA_BR_HELLO_TIME + * The time between hello packets sent by the bridge, when it is a root + * bridge or a designated bridge. Only relevant if STP is enabled. + * + * The valid values are between (1 * USER_HZ) and (10 * USER_HZ). + * The default value is (2 * USER_HZ). + * + * @IFLA_BR_MAX_AGE + * The hello packet timeout is the time until another bridge in the + * spanning tree is assumed to be dead, after reception of its last hello + * message. Only relevant if STP is enabled. + * + * The valid values are between (6 * USER_HZ) and (40 * USER_HZ). + * The default value is (20 * USER_HZ). + * + * @IFLA_BR_AGEING_TIME + * Configure the bridge's FDB entries aging time. It is the time a MAC + * address will be kept in the FDB after a packet has been received from + * that address. After this time has passed, entries are cleaned up. + * Allow values outside the 802.1 standard specification for special cases: + * + * * 0 - entry never ages (all permanent) + * * 1 - entry disappears (no persistence) + * + * The default value is (300 * USER_HZ). + * + * @IFLA_BR_STP_STATE + * Turn spanning tree protocol on (*IFLA_BR_STP_STATE* > 0) or off + * (*IFLA_BR_STP_STATE* == 0) for this bridge. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_PRIORITY + * Set this bridge's spanning tree priority, used during STP root bridge + * election. + * + * The valid values are between 0 and 65535. + * + * @IFLA_BR_VLAN_FILTERING + * Turn VLAN filtering on (*IFLA_BR_VLAN_FILTERING* > 0) or off + * (*IFLA_BR_VLAN_FILTERING* == 0). When disabled, the bridge will not + * consider the VLAN tag when handling packets. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_VLAN_PROTOCOL + * Set the protocol used for VLAN filtering. + * + * The valid values are 0x8100(802.1Q) or 0x88A8(802.1AD). The default value + * is 0x8100(802.1Q). + * + * @IFLA_BR_GROUP_FWD_MASK + * The group forwarding mask. This is the bitmask that is applied to + * decide whether to forward incoming frames destined to link-local + * addresses (of the form 01:80:C2:00:00:0X). + * + * The default value is 0, which means the bridge does not forward any + * link-local frames coming on this port. + * + * @IFLA_BR_ROOT_ID + * The bridge root id, read only. + * + * @IFLA_BR_BRIDGE_ID + * The bridge id, read only. + * + * @IFLA_BR_ROOT_PORT + * The bridge root port, read only. + * + * @IFLA_BR_ROOT_PATH_COST + * The bridge root path cost, read only. + * + * @IFLA_BR_TOPOLOGY_CHANGE + * The bridge topology change, read only. + * + * @IFLA_BR_TOPOLOGY_CHANGE_DETECTED + * The bridge topology change detected, read only. + * + * @IFLA_BR_HELLO_TIMER + * The bridge hello timer, read only. + * + * @IFLA_BR_TCN_TIMER + * The bridge tcn timer, read only. + * + * @IFLA_BR_TOPOLOGY_CHANGE_TIMER + * The bridge topology change timer, read only. + * + * @IFLA_BR_GC_TIMER + * The bridge gc timer, read only. + * + * @IFLA_BR_GROUP_ADDR + * Set the MAC address of the multicast group this bridge uses for STP. + * The address must be a link-local address in standard Ethernet MAC address + * format. It is an address of the form 01:80:C2:00:00:0X, with X in [0, 4..f]. + * + * The default value is 0. + * + * @IFLA_BR_FDB_FLUSH + * Flush bridge's fdb dynamic entries. + * + * @IFLA_BR_MCAST_ROUTER + * Set bridge's multicast router if IGMP snooping is enabled. + * The valid values are: + * + * * 0 - disabled. + * * 1 - automatic (queried). + * * 2 - permanently enabled. + * + * The default value is 1. + * + * @IFLA_BR_MCAST_SNOOPING + * Turn multicast snooping on (*IFLA_BR_MCAST_SNOOPING* > 0) or off + * (*IFLA_BR_MCAST_SNOOPING* == 0). + * + * The default value is 1. + * + * @IFLA_BR_MCAST_QUERY_USE_IFADDR + * If enabled use the bridge's own IP address as source address for IGMP + * queries (*IFLA_BR_MCAST_QUERY_USE_IFADDR* > 0) or the default of 0.0.0.0 + * (*IFLA_BR_MCAST_QUERY_USE_IFADDR* == 0). + * + * The default value is 0 (disabled). + * + * @IFLA_BR_MCAST_QUERIER + * Enable (*IFLA_BR_MULTICAST_QUERIER* > 0) or disable + * (*IFLA_BR_MULTICAST_QUERIER* == 0) IGMP querier, ie sending of multicast + * queries by the bridge. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_MCAST_HASH_ELASTICITY + * Set multicast database hash elasticity, It is the maximum chain length in + * the multicast hash table. This attribute is *deprecated* and the value + * is always 16. + * + * @IFLA_BR_MCAST_HASH_MAX + * Set maximum size of the multicast hash table + * + * The default value is 4096, the value must be a power of 2. + * + * @IFLA_BR_MCAST_LAST_MEMBER_CNT + * The Last Member Query Count is the number of Group-Specific Queries + * sent before the router assumes there are no local members. The Last + * Member Query Count is also the number of Group-and-Source-Specific + * Queries sent before the router assumes there are no listeners for a + * particular source. + * + * The default value is 2. + * + * @IFLA_BR_MCAST_STARTUP_QUERY_CNT + * The Startup Query Count is the number of Queries sent out on startup, + * separated by the Startup Query Interval. + * + * The default value is 2. + * + * @IFLA_BR_MCAST_LAST_MEMBER_INTVL + * The Last Member Query Interval is the Max Response Time inserted into + * Group-Specific Queries sent in response to Leave Group messages, and + * is also the amount of time between Group-Specific Query messages. + * + * The default value is (1 * USER_HZ). + * + * @IFLA_BR_MCAST_MEMBERSHIP_INTVL + * The interval after which the bridge will leave a group, if no membership + * reports for this group are received. + * + * The default value is (260 * USER_HZ). + * + * @IFLA_BR_MCAST_QUERIER_INTVL + * The interval between queries sent by other routers. if no queries are + * seen after this delay has passed, the bridge will start to send its own + * queries (as if *IFLA_BR_MCAST_QUERIER_INTVL* was enabled). + * + * The default value is (255 * USER_HZ). + * + * @IFLA_BR_MCAST_QUERY_INTVL + * The Query Interval is the interval between General Queries sent by + * the Querier. + * + * The default value is (125 * USER_HZ). The minimum value is (1 * USER_HZ). + * + * @IFLA_BR_MCAST_QUERY_RESPONSE_INTVL + * The Max Response Time used to calculate the Max Resp Code inserted + * into the periodic General Queries. + * + * The default value is (10 * USER_HZ). + * + * @IFLA_BR_MCAST_STARTUP_QUERY_INTVL + * The interval between queries in the startup phase. + * + * The default value is (125 * USER_HZ) / 4. The minimum value is (1 * USER_HZ). + * + * @IFLA_BR_NF_CALL_IPTABLES + * Enable (*NF_CALL_IPTABLES* > 0) or disable (*NF_CALL_IPTABLES* == 0) + * iptables hooks on the bridge. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_NF_CALL_IP6TABLES + * Enable (*NF_CALL_IP6TABLES* > 0) or disable (*NF_CALL_IP6TABLES* == 0) + * ip6tables hooks on the bridge. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_NF_CALL_ARPTABLES + * Enable (*NF_CALL_ARPTABLES* > 0) or disable (*NF_CALL_ARPTABLES* == 0) + * arptables hooks on the bridge. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_VLAN_DEFAULT_PVID + * VLAN ID applied to untagged and priority-tagged incoming packets. + * + * The default value is 1. Setting to the special value 0 makes all ports of + * this bridge not have a PVID by default, which means that they will + * not accept VLAN-untagged traffic. + * + * @IFLA_BR_PAD + * Bridge attribute padding type for netlink message. + * + * @IFLA_BR_VLAN_STATS_ENABLED + * Enable (*IFLA_BR_VLAN_STATS_ENABLED* == 1) or disable + * (*IFLA_BR_VLAN_STATS_ENABLED* == 0) per-VLAN stats accounting. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_MCAST_STATS_ENABLED + * Enable (*IFLA_BR_MCAST_STATS_ENABLED* > 0) or disable + * (*IFLA_BR_MCAST_STATS_ENABLED* == 0) multicast (IGMP/MLD) stats + * accounting. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_MCAST_IGMP_VERSION + * Set the IGMP version. + * + * The valid values are 2 and 3. The default value is 2. + * + * @IFLA_BR_MCAST_MLD_VERSION + * Set the MLD version. + * + * The valid values are 1 and 2. The default value is 1. + * + * @IFLA_BR_VLAN_STATS_PER_PORT + * Enable (*IFLA_BR_VLAN_STATS_PER_PORT* == 1) or disable + * (*IFLA_BR_VLAN_STATS_PER_PORT* == 0) per-VLAN per-port stats accounting. + * Can be changed only when there are no port VLANs configured. + * + * The default value is 0 (disabled). + * + * @IFLA_BR_MULTI_BOOLOPT + * The multi_boolopt is used to control new boolean options to avoid adding + * new netlink attributes. You can look at ``enum br_boolopt_id`` for those + * options. + * + * @IFLA_BR_MCAST_QUERIER_STATE + * Bridge mcast querier states, read only. + * + * @IFLA_BR_FDB_N_LEARNED + * The number of dynamically learned FDB entries for the current bridge, + * read only. + * + * @IFLA_BR_FDB_MAX_LEARNED + * Set the number of max dynamically learned FDB entries for the current + * bridge. + */ enum { IFLA_BR_UNSPEC, IFLA_BR_FORWARD_DELAY, @@ -507,6 +790,8 @@ enum { IFLA_BR_VLAN_STATS_PER_PORT, IFLA_BR_MULTI_BOOLOPT, IFLA_BR_MCAST_QUERIER_STATE, + IFLA_BR_FDB_N_LEARNED, + IFLA_BR_FDB_MAX_LEARNED, __IFLA_BR_MAX, }; @@ -517,11 +802,252 @@ struct ifla_bridge_id { __u8 addr[6]; /* ETH_ALEN */ }; +/** + * DOC: Bridge mode enum definition + * + * @BRIDGE_MODE_HAIRPIN + * Controls whether traffic may be sent back out of the port on which it + * was received. This option is also called reflective relay mode, and is + * used to support basic VEPA (Virtual Ethernet Port Aggregator) + * capabilities. By default, this flag is turned off and the bridge will + * not forward traffic back out of the receiving port. + */ enum { BRIDGE_MODE_UNSPEC, BRIDGE_MODE_HAIRPIN, }; +/** + * DOC: Bridge port enum definition + * + * @IFLA_BRPORT_STATE + * The operation state of the port. Here are the valid values. + * + * * 0 - port is in STP *DISABLED* state. Make this port completely + * inactive for STP. This is also called BPDU filter and could be used + * to disable STP on an untrusted port, like a leaf virtual device. + * The traffic forwarding is also stopped on this port. + * * 1 - port is in STP *LISTENING* state. Only valid if STP is enabled + * on the bridge. In this state the port listens for STP BPDUs and + * drops all other traffic frames. + * * 2 - port is in STP *LEARNING* state. Only valid if STP is enabled on + * the bridge. In this state the port will accept traffic only for the + * purpose of updating MAC address tables. + * * 3 - port is in STP *FORWARDING* state. Port is fully active. + * * 4 - port is in STP *BLOCKING* state. Only valid if STP is enabled on + * the bridge. This state is used during the STP election process. + * In this state, port will only process STP BPDUs. + * + * @IFLA_BRPORT_PRIORITY + * The STP port priority. The valid values are between 0 and 255. + * + * @IFLA_BRPORT_COST + * The STP path cost of the port. The valid values are between 1 and 65535. + * + * @IFLA_BRPORT_MODE + * Set the bridge port mode. See *BRIDGE_MODE_HAIRPIN* for more details. + * + * @IFLA_BRPORT_GUARD + * Controls whether STP BPDUs will be processed by the bridge port. By + * default, the flag is turned off to allow BPDU processing. Turning this + * flag on will disable the bridge port if a STP BPDU packet is received. + * + * If the bridge has Spanning Tree enabled, hostile devices on the network + * may send BPDU on a port and cause network failure. Setting *guard on* + * will detect and stop this by disabling the port. The port will be + * restarted if the link is brought down, or removed and reattached. + * + * @IFLA_BRPORT_PROTECT + * Controls whether a given port is allowed to become a root port or not. + * Only used when STP is enabled on the bridge. By default the flag is off. + * + * This feature is also called root port guard. If BPDU is received from a + * leaf (edge) port, it should not be elected as root port. This could + * be used if using STP on a bridge and the downstream bridges are not fully + * trusted; this prevents a hostile guest from rerouting traffic. + * + * @IFLA_BRPORT_FAST_LEAVE + * This flag allows the bridge to immediately stop multicast traffic + * forwarding on a port that receives an IGMP Leave message. It is only used + * when IGMP snooping is enabled on the bridge. By default the flag is off. + * + * @IFLA_BRPORT_LEARNING + * Controls whether a given port will learn *source* MAC addresses from + * received traffic or not. Also controls whether dynamic FDB entries + * (which can also be added by software) will be refreshed by incoming + * traffic. By default this flag is on. + * + * @IFLA_BRPORT_UNICAST_FLOOD + * Controls whether unicast traffic for which there is no FDB entry will + * be flooded towards this port. By default this flag is on. + * + * @IFLA_BRPORT_PROXYARP + * Enable proxy ARP on this port. + * + * @IFLA_BRPORT_LEARNING_SYNC + * Controls whether a given port will sync MAC addresses learned on device + * port to bridge FDB. + * + * @IFLA_BRPORT_PROXYARP_WIFI + * Enable proxy ARP on this port which meets extended requirements by + * IEEE 802.11 and Hotspot 2.0 specifications. + * + * @IFLA_BRPORT_ROOT_ID + * + * @IFLA_BRPORT_BRIDGE_ID + * + * @IFLA_BRPORT_DESIGNATED_PORT + * + * @IFLA_BRPORT_DESIGNATED_COST + * + * @IFLA_BRPORT_ID + * + * @IFLA_BRPORT_NO + * + * @IFLA_BRPORT_TOPOLOGY_CHANGE_ACK + * + * @IFLA_BRPORT_CONFIG_PENDING + * + * @IFLA_BRPORT_MESSAGE_AGE_TIMER + * + * @IFLA_BRPORT_FORWARD_DELAY_TIMER + * + * @IFLA_BRPORT_HOLD_TIMER + * + * @IFLA_BRPORT_FLUSH + * Flush bridge ports' fdb dynamic entries. + * + * @IFLA_BRPORT_MULTICAST_ROUTER + * Configure the port's multicast router presence. A port with + * a multicast router will receive all multicast traffic. + * The valid values are: + * + * * 0 disable multicast routers on this port + * * 1 let the system detect the presence of routers (default) + * * 2 permanently enable multicast traffic forwarding on this port + * * 3 enable multicast routers temporarily on this port, not depending + * on incoming queries. + * + * @IFLA_BRPORT_PAD + * + * @IFLA_BRPORT_MCAST_FLOOD + * Controls whether a given port will flood multicast traffic for which + * there is no MDB entry. By default this flag is on. + * + * @IFLA_BRPORT_MCAST_TO_UCAST + * Controls whether a given port will replicate packets using unicast + * instead of multicast. By default this flag is off. + * + * This is done by copying the packet per host and changing the multicast + * destination MAC to a unicast one accordingly. + * + * *mcast_to_unicast* works on top of the multicast snooping feature of the + * bridge. Which means unicast copies are only delivered to hosts which + * are interested in unicast and signaled this via IGMP/MLD reports previously. + * + * This feature is intended for interface types which have a more reliable + * and/or efficient way to deliver unicast packets than broadcast ones + * (e.g. WiFi). + * + * However, it should only be enabled on interfaces where no IGMPv2/MLDv1 + * report suppression takes place. IGMP/MLD report suppression issue is + * usually overcome by the network daemon (supplicant) enabling AP isolation + * and by that separating all STAs. + * + * Delivery of STA-to-STA IP multicast is made possible again by enabling + * and utilizing the bridge hairpin mode, which considers the incoming port + * as a potential outgoing port, too (see *BRIDGE_MODE_HAIRPIN* option). + * Hairpin mode is performed after multicast snooping, therefore leading + * to only deliver reports to STAs running a multicast router. + * + * @IFLA_BRPORT_VLAN_TUNNEL + * Controls whether vlan to tunnel mapping is enabled on the port. + * By default this flag is off. + * + * @IFLA_BRPORT_BCAST_FLOOD + * Controls flooding of broadcast traffic on the given port. By default + * this flag is on. + * + * @IFLA_BRPORT_GROUP_FWD_MASK + * Set the group forward mask. This is a bitmask that is applied to + * decide whether to forward incoming frames destined to link-local + * addresses. The addresses of the form are 01:80:C2:00:00:0X (defaults + * to 0, which means the bridge does not forward any link-local frames + * coming on this port). + * + * @IFLA_BRPORT_NEIGH_SUPPRESS + * Controls whether neighbor discovery (arp and nd) proxy and suppression + * is enabled on the port. By default this flag is off. + * + * @IFLA_BRPORT_ISOLATED + * Controls whether a given port will be isolated, which means it will be + * able to communicate with non-isolated ports only. By default this + * flag is off. + * + * @IFLA_BRPORT_BACKUP_PORT + * Set a backup port. If the port loses carrier all traffic will be + * redirected to the configured backup port. Set the value to 0 to disable + * it. + * + * @IFLA_BRPORT_MRP_RING_OPEN + * + * @IFLA_BRPORT_MRP_IN_OPEN + * + * @IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT + * The number of per-port EHT hosts limit. The default value is 512. + * Setting to 0 is not allowed. + * + * @IFLA_BRPORT_MCAST_EHT_HOSTS_CNT + * The current number of tracked hosts, read only. + * + * @IFLA_BRPORT_LOCKED + * Controls whether a port will be locked, meaning that hosts behind the + * port will not be able to communicate through the port unless an FDB + * entry with the unit's MAC address is in the FDB. The common use case is + * that hosts are allowed access through authentication with the IEEE 802.1X + * protocol or based on whitelists. By default this flag is off. + * + * Please note that secure 802.1X deployments should always use the + * *BR_BOOLOPT_NO_LL_LEARN* flag, to not permit the bridge to populate its + * FDB based on link-local (EAPOL) traffic received on the port. + * + * @IFLA_BRPORT_MAB + * Controls whether a port will use MAC Authentication Bypass (MAB), a + * technique through which select MAC addresses may be allowed on a locked + * port, without using 802.1X authentication. Packets with an unknown source + * MAC address generates a "locked" FDB entry on the incoming bridge port. + * The common use case is for user space to react to these bridge FDB + * notifications and optionally replace the locked FDB entry with a normal + * one, allowing traffic to pass for whitelisted MAC addresses. + * + * Setting this flag also requires *IFLA_BRPORT_LOCKED* and + * *IFLA_BRPORT_LEARNING*. *IFLA_BRPORT_LOCKED* ensures that unauthorized + * data packets are dropped, and *IFLA_BRPORT_LEARNING* allows the dynamic + * FDB entries installed by user space (as replacements for the locked FDB + * entries) to be refreshed and/or aged out. + * + * @IFLA_BRPORT_MCAST_N_GROUPS + * + * @IFLA_BRPORT_MCAST_MAX_GROUPS + * Sets the maximum number of MDB entries that can be registered for a + * given port. Attempts to register more MDB entries at the port than this + * limit allows will be rejected, whether they are done through netlink + * (e.g. the bridge tool), or IGMP or MLD membership reports. Setting a + * limit of 0 disables the limit. The default value is 0. + * + * @IFLA_BRPORT_NEIGH_VLAN_SUPPRESS + * Controls whether neighbor discovery (arp and nd) proxy and suppression is + * enabled for a given port. By default this flag is off. + * + * Note that this option only takes effect when *IFLA_BRPORT_NEIGH_SUPPRESS* + * is enabled for a given port. + * + * @IFLA_BRPORT_BACKUP_NHID + * The FDB nexthop object ID to attach to packets being redirected to a + * backup port that has VLAN tunnel mapping enabled (via the + * *IFLA_BRPORT_VLAN_TUNNEL* option). Setting a value of 0 (default) has + * the effect of not attaching any ID. + */ enum { IFLA_BRPORT_UNSPEC, IFLA_BRPORT_STATE, /* Spanning tree state */ @@ -564,6 +1090,10 @@ enum { IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, IFLA_BRPORT_LOCKED, IFLA_BRPORT_MAB, + IFLA_BRPORT_MCAST_N_GROUPS, + IFLA_BRPORT_MCAST_MAX_GROUPS, + IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + IFLA_BRPORT_BACKUP_NHID, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) @@ -630,6 +1160,7 @@ enum { IFLA_MACVLAN_MACADDR_COUNT, IFLA_MACVLAN_BC_QUEUE_LEN, IFLA_MACVLAN_BC_QUEUE_LEN_USED, + IFLA_MACVLAN_BC_CUTOFF, __IFLA_MACVLAN_MAX, }; @@ -748,6 +1279,30 @@ struct tunnel_msg { __u32 ifindex; }; +/* netkit section */ +enum netkit_action { + NETKIT_NEXT = -1, + NETKIT_PASS = 0, + NETKIT_DROP = 2, + NETKIT_REDIRECT = 7, +}; + +enum netkit_mode { + NETKIT_L2, + NETKIT_L3, +}; + +enum { + IFLA_NETKIT_UNSPEC, + IFLA_NETKIT_PEER_INFO, + IFLA_NETKIT_PRIMARY, + IFLA_NETKIT_POLICY, + IFLA_NETKIT_PEER_POLICY, + IFLA_NETKIT_MODE, + __IFLA_NETKIT_MAX, +}; +#define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1) + /* VXLAN section */ /* include statistics in the dump */ @@ -821,6 +1376,8 @@ enum { IFLA_VXLAN_TTL_INHERIT, IFLA_VXLAN_DF, IFLA_VXLAN_VNIFILTER, /* only applicable with COLLECT_METADATA mode */ + IFLA_VXLAN_LOCALBYPASS, + IFLA_VXLAN_LABEL_POLICY, /* IPv6 flow label policy; ifla_vxlan_label_policy */ __IFLA_VXLAN_MAX }; #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) @@ -838,6 +1395,13 @@ enum ifla_vxlan_df { VXLAN_DF_MAX = __VXLAN_DF_END - 1, }; +enum ifla_vxlan_label_policy { + VXLAN_LABEL_FIXED = 0, + VXLAN_LABEL_INHERIT = 1, + __VXLAN_LABEL_END, + VXLAN_LABEL_MAX = __VXLAN_LABEL_END - 1, +}; + /* GENEVE section */ enum { IFLA_GENEVE_UNSPEC, @@ -941,6 +1505,7 @@ enum { IFLA_BOND_AD_LACP_ACTIVE, IFLA_BOND_MISSED_MAX, IFLA_BOND_NS_IP6_TARGET, + IFLA_BOND_COUPLED_CONTROL, __IFLA_BOND_MAX, }; @@ -1383,7 +1948,9 @@ enum { enum { IFLA_DSA_UNSPEC, - IFLA_DSA_MASTER, + IFLA_DSA_CONDUIT, + /* Deprecated, use IFLA_DSA_CONDUIT instead */ + IFLA_DSA_MASTER = IFLA_DSA_CONDUIT, __IFLA_DSA_MAX, }; diff --git a/src/basic/linux/in.h b/src/basic/linux/in.h index 07a4cb1..e682ab6 100644 --- a/src/basic/linux/in.h +++ b/src/basic/linux/in.h @@ -162,6 +162,8 @@ struct in_addr { #define MCAST_MSFILTER 48 #define IP_MULTICAST_ALL 49 #define IP_UNICAST_IF 50 +#define IP_LOCAL_PORT_RANGE 51 +#define IP_PROTOCOL 52 #define MCAST_EXCLUDE 0 #define MCAST_INCLUDE 1 diff --git a/src/basic/linux/in6.h b/src/basic/linux/in6.h index c4c53a9..ff8d21f 100644 --- a/src/basic/linux/in6.h +++ b/src/basic/linux/in6.h @@ -145,7 +145,7 @@ struct in6_flowlabel_req { #define IPV6_TLV_PADN 1 #define IPV6_TLV_ROUTERALERT 5 #define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ -#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */ +#define IPV6_TLV_IOAM 49 /* RFC 9486 */ #define IPV6_TLV_JUMBO 194 #define IPV6_TLV_HAO 201 /* home address option */ diff --git a/src/basic/linux/magic.h b/src/basic/linux/magic.h new file mode 100644 index 0000000..1b40a96 --- /dev/null +++ b/src/basic/linux/magic.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_MAGIC_H__ +#define __LINUX_MAGIC_H__ + +#define ADFS_SUPER_MAGIC 0xadf5 +#define AFFS_SUPER_MAGIC 0xadff +#define AFS_SUPER_MAGIC 0x5346414F +#define AUTOFS_SUPER_MAGIC 0x0187 +#define CEPH_SUPER_MAGIC 0x00c36400 +#define CODA_SUPER_MAGIC 0x73757245 +#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ +#define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */ +#define DEBUGFS_MAGIC 0x64626720 +#define SECURITYFS_MAGIC 0x73636673 +#define SELINUX_MAGIC 0xf97cff8c +#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ +#define RAMFS_MAGIC 0x858458f6 /* some random number */ +#define TMPFS_MAGIC 0x01021994 +#define HUGETLBFS_MAGIC 0x958458f6 /* some random number */ +#define SQUASHFS_MAGIC 0x73717368 +#define ECRYPTFS_SUPER_MAGIC 0xf15f +#define EFS_SUPER_MAGIC 0x414A53 +#define EROFS_SUPER_MAGIC_V1 0xE0F5E1E2 +#define EXT2_SUPER_MAGIC 0xEF53 +#define EXT3_SUPER_MAGIC 0xEF53 +#define XENFS_SUPER_MAGIC 0xabba1974 +#define EXT4_SUPER_MAGIC 0xEF53 +#define BTRFS_SUPER_MAGIC 0x9123683E +#define NILFS_SUPER_MAGIC 0x3434 +#define F2FS_SUPER_MAGIC 0xF2F52010 +#define HPFS_SUPER_MAGIC 0xf995e849 +#define ISOFS_SUPER_MAGIC 0x9660 +#define JFFS2_SUPER_MAGIC 0x72b6 +#define XFS_SUPER_MAGIC 0x58465342 /* "XFSB" */ +#define PSTOREFS_MAGIC 0x6165676C +#define EFIVARFS_MAGIC 0xde5e81e4 +#define HOSTFS_SUPER_MAGIC 0x00c0ffee +#define OVERLAYFS_SUPER_MAGIC 0x794c7630 +#define FUSE_SUPER_MAGIC 0x65735546 + +#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix v2 fs, 14 char names */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix v2 fs, 30 char names */ +#define MINIX3_SUPER_MAGIC 0x4d5a /* minix v3 fs, 60 char names */ + +#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */ +#define EXFAT_SUPER_MAGIC 0x2011BAB0 +#define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */ +#define NFS_SUPER_MAGIC 0x6969 +#define OCFS2_SUPER_MAGIC 0x7461636f +#define OPENPROM_SUPER_MAGIC 0x9fa1 +#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */ +#define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */ +#define AFS_FS_MAGIC 0x6B414653 + + +#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */ + /* used by file system utilities that + look at the superblock, etc. */ +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + +#define SMB_SUPER_MAGIC 0x517B +#define CIFS_SUPER_MAGIC 0xFF534D42 /* the first four bytes of SMB PDUs */ +#define SMB2_SUPER_MAGIC 0xFE534D42 + +#define CGROUP_SUPER_MAGIC 0x27e0eb +#define CGROUP2_SUPER_MAGIC 0x63677270 + +#define RDTGROUP_SUPER_MAGIC 0x7655821 + +#define STACK_END_MAGIC 0x57AC6E9D + +#define TRACEFS_MAGIC 0x74726163 + +#define V9FS_MAGIC 0x01021997 + +#define BDEVFS_MAGIC 0x62646576 +#define DAXFS_MAGIC 0x64646178 +#define BINFMTFS_MAGIC 0x42494e4d +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#define BINDERFS_SUPER_MAGIC 0x6c6f6f70 +#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA +#define PIPEFS_MAGIC 0x50495045 +#define PROC_SUPER_MAGIC 0x9fa0 +#define SOCKFS_MAGIC 0x534F434B +#define SYSFS_MAGIC 0x62656572 +#define USBDEVICE_SUPER_MAGIC 0x9fa2 +#define MTD_INODE_FS_MAGIC 0x11307854 +#define ANON_INODE_FS_MAGIC 0x09041934 +#define BTRFS_TEST_MAGIC 0x73727279 +#define NSFS_MAGIC 0x6e736673 +#define BPF_FS_MAGIC 0xcafe4a11 +#define AAFS_MAGIC 0x5a3c69f0 +#define ZONEFS_MAGIC 0x5a4f4653 + +/* Since UDF 2.01 is ISO 13346 based... */ +#define UDF_SUPER_MAGIC 0x15013346 +#define DMA_BUF_MAGIC 0x444d4142 /* "DMAB" */ +#define DEVMEM_MAGIC 0x454d444d /* "DMEM" */ +#define SECRETMEM_MAGIC 0x5345434d /* "SECM" */ +#define PID_FS_MAGIC 0x50494446 /* "PIDF" */ + +#endif /* __LINUX_MAGIC_H__ */ diff --git a/src/basic/linux/netfilter.h b/src/basic/linux/netfilter.h new file mode 100644 index 0000000..30c045b --- /dev/null +++ b/src/basic/linux/netfilter.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETFILTER_H +#define __LINUX_NETFILTER_H + +#include <linux/types.h> + +#include <linux/in.h> +#include <linux/in6.h> + +/* Responses from hook functions. */ +#define NF_DROP 0 +#define NF_ACCEPT 1 +#define NF_STOLEN 2 +#define NF_QUEUE 3 +#define NF_REPEAT 4 +#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */ +#define NF_MAX_VERDICT NF_STOP + +/* we overload the higher bits for encoding auxiliary data such as the queue + * number or errno values. Not nice, but better than additional function + * arguments. */ +#define NF_VERDICT_MASK 0x000000ff + +/* extra verdict flags have mask 0x0000ff00 */ +#define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000 + +/* queue number (NF_QUEUE) or errno (NF_DROP) */ +#define NF_VERDICT_QMASK 0xffff0000 +#define NF_VERDICT_QBITS 16 + +#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE) + +#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP) + +/* only for userspace compatibility */ + +/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */ +#define NF_VERDICT_BITS 16 + +enum nf_inet_hooks { + NF_INET_PRE_ROUTING, + NF_INET_LOCAL_IN, + NF_INET_FORWARD, + NF_INET_LOCAL_OUT, + NF_INET_POST_ROUTING, + NF_INET_NUMHOOKS, + NF_INET_INGRESS = NF_INET_NUMHOOKS, +}; + +enum nf_dev_hooks { + NF_NETDEV_INGRESS, + NF_NETDEV_EGRESS, + NF_NETDEV_NUMHOOKS +}; + +enum { + NFPROTO_UNSPEC = 0, + NFPROTO_INET = 1, + NFPROTO_IPV4 = 2, + NFPROTO_ARP = 3, + NFPROTO_NETDEV = 5, + NFPROTO_BRIDGE = 7, + NFPROTO_IPV6 = 10, + NFPROTO_DECNET = 12, + NFPROTO_NUMPROTO, +}; + +union nf_inet_addr { + __u32 all[4]; + __be32 ip; + __be32 ip6[4]; + struct in_addr in; + struct in6_addr in6; +}; + +#endif /* __LINUX_NETFILTER_H */ diff --git a/src/basic/linux/netfilter/nf_tables.h b/src/basic/linux/netfilter/nf_tables.h index cfa844d..aa4094c 100644 --- a/src/basic/linux/netfilter/nf_tables.h +++ b/src/basic/linux/netfilter/nf_tables.h @@ -98,6 +98,14 @@ enum nft_verdicts { * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes) * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes) * @NFT_MSG_GETRULE_RESET: get rules and reset stateful expressions (enum nft_obj_attributes) + * @NFT_MSG_DESTROYTABLE: destroy a table (enum nft_table_attributes) + * @NFT_MSG_DESTROYCHAIN: destroy a chain (enum nft_chain_attributes) + * @NFT_MSG_DESTROYRULE: destroy a rule (enum nft_rule_attributes) + * @NFT_MSG_DESTROYSET: destroy a set (enum nft_set_attributes) + * @NFT_MSG_DESTROYSETELEM: destroy a set element (enum nft_set_elem_attributes) + * @NFT_MSG_DESTROYOBJ: destroy a stateful object (enum nft_object_attributes) + * @NFT_MSG_DESTROYFLOWTABLE: destroy flow table (enum nft_flowtable_attributes) + * @NFT_MSG_GETSETELEM_RESET: get set elements and reset attached stateful expressions (enum nft_set_elem_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -126,6 +134,14 @@ enum nf_tables_msg_types { NFT_MSG_GETFLOWTABLE, NFT_MSG_DELFLOWTABLE, NFT_MSG_GETRULE_RESET, + NFT_MSG_DESTROYTABLE, + NFT_MSG_DESTROYCHAIN, + NFT_MSG_DESTROYRULE, + NFT_MSG_DESTROYSET, + NFT_MSG_DESTROYSETELEM, + NFT_MSG_DESTROYOBJ, + NFT_MSG_DESTROYFLOWTABLE, + NFT_MSG_GETSETELEM_RESET, NFT_MSG_MAX, }; @@ -163,13 +179,17 @@ enum nft_hook_attributes { * enum nft_table_flags - nf_tables table flags * * @NFT_TABLE_F_DORMANT: this table is not active + * @NFT_TABLE_F_OWNER: this table is owned by a process + * @NFT_TABLE_F_PERSIST: this table shall outlive its owner */ enum nft_table_flags { NFT_TABLE_F_DORMANT = 0x1, NFT_TABLE_F_OWNER = 0x2, + NFT_TABLE_F_PERSIST = 0x4, }; #define NFT_TABLE_F_MASK (NFT_TABLE_F_DORMANT | \ - NFT_TABLE_F_OWNER) + NFT_TABLE_F_OWNER | \ + NFT_TABLE_F_PERSIST) /** * enum nft_table_attributes - nf_tables table netlink attributes @@ -247,6 +267,7 @@ enum nft_chain_attributes { * @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN) * @NFTA_RULE_ID: uniquely identifies a rule in a transaction (NLA_U32) * @NFTA_RULE_POSITION_ID: transaction unique identifier of the previous rule (NLA_U32) + * @NFTA_RULE_CHAIN_ID: add the rule to chain by ID, alternative to @NFTA_RULE_CHAIN (NLA_U32) */ enum nft_rule_attributes { NFTA_RULE_UNSPEC, @@ -268,9 +289,11 @@ enum nft_rule_attributes { /** * enum nft_rule_compat_flags - nf_tables rule compat flags * + * @NFT_RULE_COMPAT_F_UNUSED: unused * @NFT_RULE_COMPAT_F_INV: invert the check result */ enum nft_rule_compat_flags { + NFT_RULE_COMPAT_F_UNUSED = (1 << 0), NFT_RULE_COMPAT_F_INV = (1 << 1), NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV, }; @@ -671,7 +694,7 @@ enum nft_range_ops { * enum nft_range_attributes - nf_tables range expression netlink attributes * * @NFTA_RANGE_SREG: source register of data to compare (NLA_U32: nft_registers) - * @NFTA_RANGE_OP: cmp operation (NLA_U32: nft_cmp_ops) + * @NFTA_RANGE_OP: cmp operation (NLA_U32: nft_range_ops) * @NFTA_RANGE_FROM_DATA: data range from (NLA_NESTED: nft_data_attributes) * @NFTA_RANGE_TO_DATA: data range to (NLA_NESTED: nft_data_attributes) */ @@ -845,12 +868,14 @@ enum nft_exthdr_flags { * @NFT_EXTHDR_OP_TCP: match against tcp options * @NFT_EXTHDR_OP_IPV4: match against ipv4 options * @NFT_EXTHDR_OP_SCTP: match against sctp chunks + * @NFT_EXTHDR_OP_DCCP: match against dccp otions */ enum nft_exthdr_op { NFT_EXTHDR_OP_IPV6, NFT_EXTHDR_OP_TCPOPT, NFT_EXTHDR_OP_IPV4, NFT_EXTHDR_OP_SCTP, + NFT_EXTHDR_OP_DCCP, __NFT_EXTHDR_OP_MAX }; #define NFT_EXTHDR_OP_MAX (__NFT_EXTHDR_OP_MAX - 1) @@ -864,7 +889,7 @@ enum nft_exthdr_op { * @NFTA_EXTHDR_LEN: extension header length (NLA_U32) * @NFTA_EXTHDR_FLAGS: extension header flags (NLA_U32) * @NFTA_EXTHDR_OP: option match type (NLA_U32) - * @NFTA_EXTHDR_SREG: option match type (NLA_U32) + * @NFTA_EXTHDR_SREG: source register (NLA_U32: nft_registers) */ enum nft_exthdr_attributes { NFTA_EXTHDR_UNSPEC, @@ -917,6 +942,7 @@ enum nft_exthdr_attributes { * @NFT_META_TIME_HOUR: hour of day (in seconds) * @NFT_META_SDIF: slave device interface index * @NFT_META_SDIFNAME: slave device interface name + * @NFT_META_BRI_BROUTE: packet br_netfilter_broute bit */ enum nft_meta_keys { NFT_META_LEN, @@ -955,6 +981,7 @@ enum nft_meta_keys { NFT_META_TIME_HOUR, NFT_META_SDIF, NFT_META_SDIFNAME, + NFT_META_BRI_BROUTE, __NFT_META_IIFTYPE, }; @@ -1246,10 +1273,10 @@ enum nft_last_attributes { /** * enum nft_log_attributes - nf_tables log expression netlink attributes * - * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32) + * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U16) * @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING) * @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32) - * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32) + * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U16) * @NFTA_LOG_LEVEL: log level (NLA_U32) * @NFTA_LOG_FLAGS: logging flags (NLA_U32) */ diff --git a/src/basic/linux/netlink.h b/src/basic/linux/netlink.h index e2ae82e..f87aaf2 100644 --- a/src/basic/linux/netlink.h +++ b/src/basic/linux/netlink.h @@ -298,6 +298,8 @@ struct nla_bitfield32 { * entry has attributes again, the policy for those inner ones * and the corresponding maxtype may be specified. * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute + * @NL_ATTR_TYPE_SINT: 32-bit or 64-bit signed attribute, aligned to 4B + * @NL_ATTR_TYPE_UINT: 32-bit or 64-bit unsigned attribute, aligned to 4B */ enum netlink_attribute_type { NL_ATTR_TYPE_INVALID, @@ -322,6 +324,9 @@ enum netlink_attribute_type { NL_ATTR_TYPE_NESTED_ARRAY, NL_ATTR_TYPE_BITFIELD32, + + NL_ATTR_TYPE_SINT, + NL_ATTR_TYPE_UINT, }; /** diff --git a/src/basic/linux/nexthop.h b/src/basic/linux/nexthop.h index d8ffa8c..dd8787f 100644 --- a/src/basic/linux/nexthop.h +++ b/src/basic/linux/nexthop.h @@ -30,6 +30,9 @@ enum { #define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1) +#define NHA_OP_FLAG_DUMP_STATS BIT(0) +#define NHA_OP_FLAG_DUMP_HW_STATS BIT(1) + enum { NHA_UNSPEC, NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */ @@ -60,6 +63,18 @@ enum { /* nested; nexthop bucket attributes */ NHA_RES_BUCKET, + /* u32; operation-specific flags */ + NHA_OP_FLAGS, + + /* nested; nexthop group stats */ + NHA_GROUP_STATS, + + /* u32; nexthop hardware stats enable */ + NHA_HW_STATS_ENABLE, + + /* u32; read-only; whether any driver collects HW stats */ + NHA_HW_STATS_USED, + __NHA_MAX, }; @@ -101,4 +116,34 @@ enum { #define NHA_RES_BUCKET_MAX (__NHA_RES_BUCKET_MAX - 1) +enum { + NHA_GROUP_STATS_UNSPEC, + + /* nested; nexthop group entry stats */ + NHA_GROUP_STATS_ENTRY, + + __NHA_GROUP_STATS_MAX, +}; + +#define NHA_GROUP_STATS_MAX (__NHA_GROUP_STATS_MAX - 1) + +enum { + NHA_GROUP_STATS_ENTRY_UNSPEC, + + /* u32; nexthop id of the nexthop group entry */ + NHA_GROUP_STATS_ENTRY_ID, + + /* uint; number of packets forwarded via the nexthop group entry */ + NHA_GROUP_STATS_ENTRY_PACKETS, + + /* uint; number of packets forwarded via the nexthop group entry in + * hardware + */ + NHA_GROUP_STATS_ENTRY_PACKETS_HW, + + __NHA_GROUP_STATS_ENTRY_MAX, +}; + +#define NHA_GROUP_STATS_ENTRY_MAX (__NHA_GROUP_STATS_ENTRY_MAX - 1) + #endif diff --git a/src/basic/linux/nl80211.h b/src/basic/linux/nl80211.h index c14a91b..f23ecbd 100644 --- a/src/basic/linux/nl80211.h +++ b/src/basic/linux/nl80211.h @@ -11,7 +11,7 @@ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> * Copyright 2008 Colin McCabe <colin@cozybit.com> * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -72,7 +72,7 @@ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: * - a setup station entry is added, not yet authorized, without any rate - * or capability information, this just exists to avoid race conditions + * or capability information; this just exists to avoid race conditions * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid * to add rate and capability information to the station and at the same * time mark it authorized. @@ -87,7 +87,7 @@ * DOC: Frame transmission/registration support * * Frame transmission and registration support exists to allow userspace - * management entities such as wpa_supplicant react to management frames + * management entities such as wpa_supplicant to react to management frames * that are not being handled by the kernel. This includes, for example, * certain classes of action frames that cannot be handled in the kernel * for various reasons. @@ -113,7 +113,7 @@ * * Frame transmission allows userspace to send for example the required * responses to action frames. It is subject to some sanity checking, - * but many frames can be transmitted. When a frame was transmitted, its + * but many frames can be transmitted. When a frame is transmitted, its * status is indicated to the sending socket. * * For more technical details, see the corresponding command descriptions @@ -123,7 +123,7 @@ /** * DOC: Virtual interface / concurrency capabilities * - * Some devices are able to operate with virtual MACs, they can have + * Some devices are able to operate with virtual MACs; they can have * more than one virtual interface. The capability handling for this * is a bit complex though, as there may be a number of restrictions * on the types of concurrency that are supported. @@ -135,7 +135,7 @@ * Once concurrency is desired, more attributes must be observed: * To start with, since some interface types are purely managed in * software, like the AP-VLAN type in mac80211 for example, there's - * an additional list of these, they can be added at any time and + * an additional list of these; they can be added at any time and * are only restricted by some semantic restrictions (e.g. AP-VLAN * cannot be added without a corresponding AP interface). This list * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. @@ -164,17 +164,17 @@ * Packet coalesce feature helps to reduce number of received interrupts * to host by buffering these packets in firmware/hardware for some * predefined time. Received interrupt will be generated when one of the - * following events occur. + * following events occurs. * a) Expiration of hardware timer whose expiration time is set to maximum * coalescing delay of matching coalesce rule. - * b) Coalescing buffer in hardware reaches it's limit. + * b) Coalescing buffer in hardware reaches its limit. * c) Packet doesn't match any of the configured coalesce rules. * * User needs to configure following parameters for creating a coalesce * rule. * a) Maximum coalescing delay * b) List of packet patterns which needs to be matched - * c) Condition for coalescence. pattern 'match' or 'no match' + * c) Condition for coalescence: pattern 'match' or 'no match' * Multiple such rules can be created. */ @@ -213,7 +213,7 @@ /** * DOC: FILS shared key authentication offload * - * FILS shared key authentication offload can be advertized by drivers by + * FILS shared key authentication offload can be advertised by drivers by * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support * FILS shared key authentication offload should be able to construct the * authentication and association frames for FILS shared key authentication and @@ -239,7 +239,7 @@ * The PMKSA can be maintained in userspace persistently so that it can be used * later after reboots or wifi turn off/on also. * - * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertised by a FILS * capable AP supporting PMK caching. It specifies the scope within which the * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based @@ -290,12 +290,12 @@ * If the configuration needs to be applied for specific peer then the MAC * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the * configuration will be applied for all the connected peers in the vif except - * any peers that have peer specific configuration for the TID by default; if - * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values + * any peers that have peer-specific configuration for the TID by default; if + * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer-specific values * will be overwritten. * - * All this configuration is valid only for STA's current connection - * i.e. the configuration will be reset to default when the STA connects back + * All this configuration is valid only for STA's current connection, + * i.e., the configuration will be reset to default when the STA connects back * after disconnection/roaming, and this configuration will be cleared when * the interface goes down. */ @@ -326,7 +326,7 @@ /** * DOC: Multi-Link Operation * - * In Multi-Link Operation, a connection between to MLDs utilizes multiple + * In Multi-Link Operation, a connection between two MLDs utilizes multiple * links. To use this in nl80211, various commands and responses now need * to or will include the new %NL80211_ATTR_MLO_LINKS attribute. * Additionally, various commands that need to operate on a specific link @@ -335,6 +335,15 @@ */ /** + * DOC: OWE DH IE handling offload + * + * By setting @NL80211_EXT_FEATURE_OWE_OFFLOAD flag, drivers can indicate + * kernel/application space to avoid DH IE handling. When this flag is + * advertised, the driver/device will take care of DH IE inclusion and + * processing of peer DH IE to generate PMK. + */ + +/** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors @@ -424,11 +433,13 @@ * interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC * or, if no MAC address given, all stations, on the interface identified - * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and + * by %NL80211_ATTR_IFINDEX. For MLD station, MLD address is used in + * %NL80211_ATTR_MAC. %NL80211_ATTR_MGMT_SUBTYPE and * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type * of disconnection indication should be sent to the station * (Deauthentication or Disassociation frame and reason code for that - * frame). + * frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove + * stations connected and using at least that link as one of its links. * * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by @@ -511,7 +522,7 @@ * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified, * scheduled scan will run in an infinite loop with the specified interval. - * These attributes are mutually exculsive, + * These attributes are mutually exclusive, * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if * NL80211_ATTR_SCHED_SCAN_PLANS is defined. * If for some reason scheduled scan is aborted by the driver, all scan @@ -542,7 +553,7 @@ * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface * is brought down while a scheduled scan was running. * - * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * @NL80211_CMD_GET_SURVEY: get survey results, e.g. channel occupation * or noise level * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) @@ -553,12 +564,13 @@ * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier - * advertized by a FILS capable AP identifying the scope of PMKSA in an + * advertised by a FILS capable AP identifying the scope of PMKSA in an * ESS. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS - * authentication. + * authentication. Additionally in case of SAE offload and OWE offloads + * PMKSA entry can be deleted using %NL80211_ATTR_SSID. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain @@ -597,7 +609,7 @@ * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify * the SSID (mainly for association, but is included in authentication * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ + - * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the + * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequency of the * channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the * authentication type. %NL80211_ATTR_IE is used to define IEs * (VendorSpecificInfo, but also including RSN IE and FT IEs) to be added @@ -806,7 +818,7 @@ * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ * and the attributes determining channel width) the given interface - * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. + * (identified by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while @@ -878,7 +890,7 @@ * inform userspace of the new replay counter. * * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace - * of PMKSA caching dandidates. + * of PMKSA caching candidates. * * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). * In addition, this can be used as an event to request userspace to take @@ -914,7 +926,7 @@ * * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface * by sending a null data frame to it and reporting when the frame is - * acknowleged. This is used to allow timing out inactive clients. Uses + * acknowledged. This is used to allow timing out inactive clients. Uses * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a * direct reply with an %NL80211_ATTR_COOKIE that is later used to match * up the event with the request. The event includes the same data and @@ -1125,11 +1137,15 @@ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously * configured PMK for the authenticator address identified by * %NL80211_ATTR_MAC. - * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was - * completed successfully. Drivers that support 4 way handshake offload - * should send this event after indicating 802.1X FT assocation with - * %NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT - * should be indicated instead. + * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates port is authorized and + * open for regular data traffic. For STA/P2P-client, this event is sent + * with AP MAC address and for AP/P2P-GO, the event carries the STA/P2P- + * client MAC address. + * Drivers that support 4 way handshake offload should send this event for + * STA/P2P-client after successful 4-way HS or after 802.1X FT following + * NL80211_CMD_CONNECT or NL80211_CMD_ROAM. Drivers using AP/P2P-GO 4-way + * handshake offload should send this event on successful completion of + * 4-way handshake with the peer (STA/P2P-client). * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request * and RX notification. This command is used both as a request to transmit * a control port frame and as a notification that a control port frame @@ -1166,6 +1182,23 @@ * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH * command interface. * + * Host driver sends MLD address of the AP with %NL80211_ATTR_MLD_ADDR in + * %NL80211_CMD_EXTERNAL_AUTH event to indicate user space to enable MLO + * during the authentication offload in STA mode while connecting to MLD + * APs. Host driver should check %NL80211_ATTR_MLO_SUPPORT flag capability + * in %NL80211_CMD_CONNECT to know whether the user space supports enabling + * MLO during the authentication offload or not. + * User space should enable MLO during the authentication only when it + * receives the AP MLD address in authentication offload request. User + * space shouldn't enable MLO when the authentication offload request + * doesn't indicate the AP MLD address even if the AP is MLO capable. + * User space should use %NL80211_ATTR_MLD_ADDR as peer's MLD address and + * interface address identified by %NL80211_ATTR_IFINDEX as self MLD + * address. User space and host driver to use MLD addresses in RA, TA and + * BSSID fields of the frames between them, and host driver translates the + * MLD addresses to/from link addresses based on the link chosen for the + * authentication. + * * Host driver reports this status on an authentication failure to the * user space through the connect result as the user space would have * initiated the connection through the connect request. @@ -1281,6 +1314,26 @@ * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station * + * @NL80211_CMD_SET_HW_TIMESTAMP: Enable/disable HW timestamping of Timing + * measurement and Fine timing measurement frames. If %NL80211_ATTR_MAC + * is included, enable/disable HW timestamping only for frames to/from the + * specified MAC address. Otherwise enable/disable HW timestamping for + * all TM/FTM frames (including ones that were enabled with specific MAC + * address). If %NL80211_ATTR_HW_TIMESTAMP_ENABLED is not included, disable + * HW timestamping. + * The number of peers that HW timestamping can be enabled for concurrently + * is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS. + * + * @NL80211_CMD_LINKS_REMOVED: Notify userspace about the removal of STA MLD + * setup links due to AP MLD removing the corresponding affiliated APs with + * Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide + * information about the removed STA MLD setup links. + * + * @NL80211_CMD_SET_TID_TO_LINK_MAPPING: Set the TID to Link Mapping for a + * non-AP MLD station. The %NL80211_ATTR_MLO_TTLM_DLINK and + * %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the + * TID to Link mapping for downlink/uplink traffic. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1532,6 +1585,12 @@ enum nl80211_commands { NL80211_CMD_MODIFY_LINK_STA, NL80211_CMD_REMOVE_LINK_STA, + NL80211_CMD_SET_HW_TIMESTAMP, + + NL80211_CMD_LINKS_REMOVED, + + NL80211_CMD_SET_TID_TO_LINK_MAPPING, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1789,7 +1848,7 @@ enum nl80211_commands { * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER * flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth - * frames are not forwared over the control port. + * frames are not forwarded over the control port. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. @@ -1926,10 +1985,10 @@ enum nl80211_commands { * bit. Depending on which antennas are selected in the bitmap, 802.11n * drivers can derive which chainmasks to use (if all antennas belonging to * a particular chain are disabled this chain should be disabled) and if - * a chain has diversity antennas wether diversity should be used or not. + * a chain has diversity antennas whether diversity should be used or not. * HT capabilities (STBC, TX Beamforming, Antenna selection) can be * derived from the available chains after applying the antenna mask. - * Non-802.11n drivers can derive wether to use diversity or not. + * Non-802.11n drivers can derive whether to use diversity or not. * Drivers may reject configurations or RX/TX mask combinations they cannot * support by returning -EINVAL. * @@ -2499,7 +2558,7 @@ enum nl80211_commands { * from successful FILS authentication and is used with * %NL80211_CMD_CONNECT. * - * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertised by a FILS AP * identifying the scope of PMKSAs. This is used with * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. * @@ -2653,11 +2712,13 @@ enum nl80211_commands { * * @NL80211_ATTR_FILS_DISCOVERY: Optional parameter to configure FILS * discovery. It is a nested attribute, see - * &enum nl80211_fils_discovery_attributes. + * &enum nl80211_fils_discovery_attributes. Userspace should pass an empty + * nested attribute to disable this feature and delete the templates. * * @NL80211_ATTR_UNSOL_BCAST_PROBE_RESP: Optional parameter to configure * unsolicited broadcast probe response. It is a nested attribute, see - * &enum nl80211_unsol_bcast_probe_resp_attributes. + * &enum nl80211_unsol_bcast_probe_resp_attributes. Userspace should pass an empty + * nested attribute to disable this feature and delete the templates. * * @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) @@ -2751,6 +2812,50 @@ enum nl80211_commands { * the incoming frame RX timestamp. * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent * (re)associations. + * + * @NL80211_ATTR_PUNCT_BITMAP: (u32) Preamble puncturing bitmap, lowest + * bit corresponds to the lowest 20 MHz channel. Each bit set to 1 + * indicates that the sub-channel is punctured. Higher 16 bits are + * reserved. + * + * @NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: Maximum number of peers that HW + * timestamping can be enabled for concurrently (u16), a wiphy attribute. + * A value of 0xffff indicates setting for all peers (i.e. not specifying + * an address with %NL80211_CMD_SET_HW_TIMESTAMP) is supported. + * @NL80211_ATTR_HW_TIMESTAMP_ENABLED: Indicates whether HW timestamping should + * be enabled or not (flag attribute). + * + * @NL80211_ATTR_EMA_RNR_ELEMS: Optional nested attribute for + * reduced neighbor report (RNR) elements. This attribute can be used + * only when NL80211_MBSSID_CONFIG_ATTR_EMA is enabled. + * Userspace is responsible for splitting the RNR into multiple + * elements such that each element excludes the non-transmitting + * profiles already included in the MBSSID element + * (%NL80211_ATTR_MBSSID_ELEMS) at the same index. Each EMA beacon + * will be generated by adding MBSSID and RNR elements at the same + * index. If the userspace includes more RNR elements than number of + * MBSSID elements then these will be added in every EMA beacon. + * + * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is + * disabled. + * + * @NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA: Include BSS usage data, i.e. + * include BSSes that can only be used in restricted scenarios and/or + * cannot be used at all. + * + * @NL80211_ATTR_MLO_TTLM_DLINK: Binary attribute specifying the downlink TID to + * link mapping. The length is 8 * sizeof(u16). For each TID the link + * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element) + * in Draft P802.11be_D4.0. + * @NL80211_ATTR_MLO_TTLM_ULINK: Binary attribute specifying the uplink TID to + * link mapping. The length is 8 * sizeof(u16). For each TID the link + * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element) + * in Draft P802.11be_D4.0. + * + * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with + * %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs + * are used on this connection + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3280,6 +3385,22 @@ enum nl80211_attrs { NL80211_ATTR_RX_HW_TIMESTAMP, NL80211_ATTR_TD_BITMAP, + NL80211_ATTR_PUNCT_BITMAP, + + NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS, + NL80211_ATTR_HW_TIMESTAMP_ENABLED, + + NL80211_ATTR_EMA_RNR_ELEMS, + + NL80211_ATTR_MLO_LINK_DISABLED, + + NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA, + + NL80211_ATTR_MLO_TTLM_DLINK, + NL80211_ATTR_MLO_TTLM_ULINK, + + NL80211_ATTR_ASSOC_SPP_AMSDU, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3420,6 +3541,7 @@ enum nl80211_iftype { * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a * previously added station into associated state + * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -3432,6 +3554,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, NL80211_STA_FLAG_ASSOCIATED, + NL80211_STA_FLAG_SPP_AMSDU, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, @@ -3606,6 +3729,13 @@ enum nl80211_eht_ru_alloc { * (u8, see &enum nl80211_eht_gi) * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then * non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc) + * @NL80211_RATE_INFO_S1G_MCS: S1G MCS index (u8, 0-10) + * @NL80211_RATE_INFO_S1G_NSS: S1G NSS value (u8, 1-4) + * @NL80211_RATE_INFO_1_MHZ_WIDTH: 1 MHz S1G rate + * @NL80211_RATE_INFO_2_MHZ_WIDTH: 2 MHz S1G rate + * @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate + * @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate + * @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -3632,6 +3762,13 @@ enum nl80211_rate_info { NL80211_RATE_INFO_EHT_NSS, NL80211_RATE_INFO_EHT_GI, NL80211_RATE_INFO_EHT_RU_ALLOC, + NL80211_RATE_INFO_S1G_MCS, + NL80211_RATE_INFO_S1G_NSS, + NL80211_RATE_INFO_1_MHZ_WIDTH, + NL80211_RATE_INFO_2_MHZ_WIDTH, + NL80211_RATE_INFO_4_MHZ_WIDTH, + NL80211_RATE_INFO_8_MHZ_WIDTH, + NL80211_RATE_INFO_16_MHZ_WIDTH, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -4000,6 +4137,10 @@ enum nl80211_band_iftype_attr { * @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes * the allowed channel bandwidth configurations. * Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13. + * @NL80211_BAND_ATTR_S1G_MCS_NSS_SET: S1G capabilities, supported S1G-MCS and NSS + * set subfield, as in the S1G information IE, 5 bytes + * @NL80211_BAND_ATTR_S1G_CAPA: S1G capabilities information subfield as in the + * S1G information IE, 10 bytes * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ @@ -4020,6 +4161,9 @@ enum nl80211_band_attr { NL80211_BAND_ATTR_EDMG_CHANNELS, NL80211_BAND_ATTR_EDMG_BW_CONFIG, + NL80211_BAND_ATTR_S1G_MCS_NSS_SET, + NL80211_BAND_ATTR_S1G_CAPA, + /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 @@ -4065,7 +4209,7 @@ enum nl80211_wmm_rule { * (100 * dBm). * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS * (enum nl80211_dfs_state) - * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long + * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in milliseconds for how long * this channel is in this DFS state. * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this * channel as the control channel @@ -4119,6 +4263,19 @@ enum nl80211_wmm_rule { * as the primary or any of the secondary channels isn't possible * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel * in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that + * is allowed on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_DFS_CONCURRENT: Operation on this channel is + * allowed for peer-to-peer or adhoc communication under the control + * of a DFS master which operates on the same channel (FCC-594280 D01 + * Section B.3). Should be used together with %NL80211_RRF_DFS only. + * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP + * not allowed using this channel + * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP + * not allowed using this channel + * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor + * mode despite other (regulatory) restrictions, even if the channel is + * otherwise completely disabled. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -4157,6 +4314,11 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_16MHZ, NL80211_FREQUENCY_ATTR_NO_320MHZ, NL80211_FREQUENCY_ATTR_NO_EHT, + NL80211_FREQUENCY_ATTR_PSD, + NL80211_FREQUENCY_ATTR_DFS_CONCURRENT, + NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT, + NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT, + NL80211_FREQUENCY_ATTR_CAN_MONITOR, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -4169,6 +4331,10 @@ enum nl80211_frequency_attr { #define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR #define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \ NL80211_FREQUENCY_ATTR_IR_CONCURRENT +#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \ + NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT +#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \ + NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT /** * enum nl80211_bitrate_attr - bitrate attributes @@ -4257,6 +4423,8 @@ enum nl80211_reg_type { * a given frequency range. The value is in mBm (100 * dBm). * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. * If not present or 0 default CAC time will be used. + * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm). + * This could be negative. * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number * currently defined * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use @@ -4274,6 +4442,8 @@ enum nl80211_reg_rule_attr { NL80211_ATTR_DFS_CAC_TIME, + NL80211_ATTR_POWER_RULE_PSD, + /* keep last */ __NL80211_REG_RULE_ATTR_AFTER_LAST, NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 @@ -4302,14 +4472,7 @@ enum nl80211_reg_rule_attr { * value as specified by &struct nl80211_bss_select_rssi_adjust. * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching * (this cannot be used together with SSID). - * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the - * band specific minimum rssi thresholds for the bands defined in - * enum nl80211_band. The minimum rssi threshold value(s32) specific to a - * band shall be encapsulated in attribute with type value equals to one - * of the NL80211_BAND_* defined in enum nl80211_band. For example, the - * minimum rssi threshold value for 2.4GHZ band shall be encapsulated - * within an attribute of type NL80211_BAND_2GHZ. And one or more of such - * attributes will be nested within this attribute. + * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -4322,7 +4485,7 @@ enum nl80211_sched_scan_match_attr { NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST, NL80211_SCHED_SCAN_MATCH_ATTR_BSSID, - NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, + NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */ /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -4356,6 +4519,14 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed * @NL80211_RRF_NO_HE: HE operation not allowed * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed + * @NL80211_RRF_NO_EHT: EHT operation not allowed + * @NL80211_RRF_PSD: Ruleset has power spectral density value + * @NL80211_RRF_DFS_CONCURRENT: Operation on this channel is allowed for + peer-to-peer or adhoc communication under the control of a DFS master + which operates on the same channel (FCC-594280 D01 Section B.3). + Should be used together with %NL80211_RRF_DFS only. + * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed + * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, @@ -4375,6 +4546,11 @@ enum nl80211_reg_rule_flags { NL80211_RRF_NO_160MHZ = 1<<16, NL80211_RRF_NO_HE = 1<<17, NL80211_RRF_NO_320MHZ = 1<<18, + NL80211_RRF_NO_EHT = 1<<19, + NL80211_RRF_PSD = 1<<20, + NL80211_RRF_DFS_CONCURRENT = 1<<21, + NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1<<22, + NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1<<23, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR @@ -4383,6 +4559,8 @@ enum nl80211_reg_rule_flags { #define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\ NL80211_RRF_NO_HT40PLUS) #define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT +#define NL80211_RRF_NO_UHB_VLP_CLIENT NL80211_RRF_NO_6GHZ_VLP_CLIENT +#define NL80211_RRF_NO_UHB_AFC_CLIENT NL80211_RRF_NO_6GHZ_AFC_CLIENT /* For backport compatibility with older userspace */ #define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) @@ -4912,6 +5090,36 @@ enum nl80211_bss_scan_width { }; /** + * enum nl80211_bss_use_for - bitmap indicating possible BSS use + * @NL80211_BSS_USE_FOR_NORMAL: Use this BSS for normal "connection", + * including IBSS/MBSS depending on the type. + * @NL80211_BSS_USE_FOR_MLD_LINK: This BSS can be used as a link in an + * MLO connection. Note that for an MLO connection, all links including + * the assoc link must have this flag set, and the assoc link must + * additionally have %NL80211_BSS_USE_FOR_NORMAL set. + */ +enum nl80211_bss_use_for { + NL80211_BSS_USE_FOR_NORMAL = 1 << 0, + NL80211_BSS_USE_FOR_MLD_LINK = 1 << 1, +}; + +/** + * enum nl80211_bss_cannot_use_reasons - reason(s) connection to a + * BSS isn't possible + * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't + * supported by the device, and this BSS entry represents one. + * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting + * the AP power type (SP, VLP, AP) that the AP uses. + */ +enum nl80211_bss_cannot_use_reasons { + NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 1 << 0, + NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH = 1 << 1, +}; + +#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \ + NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH + +/** * enum nl80211_bss - netlink attributes for a BSS * * @__NL80211_BSS_INVALID: invalid @@ -4942,7 +5150,7 @@ enum nl80211_bss_scan_width { * elements from a Beacon frame (bin); not present if no Beacon frame has * yet been received * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel - * (u32, enum nl80211_bss_scan_width) + * (u32, enum nl80211_bss_scan_width) - No longer used! * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64) * (not present if no beacon frame has been received yet) * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and @@ -4963,6 +5171,14 @@ enum nl80211_bss_scan_width { * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8). * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it. + * @NL80211_BSS_USE_FOR: u32 bitmap attribute indicating what the BSS can be + * used for, see &enum nl80211_bss_use_for. + * @NL80211_BSS_CANNOT_USE_REASONS: Indicates the reason that this BSS cannot + * be used for all or some of the possible uses by the device reporting it, + * even though its presence was detected. + * This is a u64 attribute containing a bitmap of values from + * &enum nl80211_cannot_use_reasons, note that the attribute may be missing + * if no reasons are specified. * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -4990,6 +5206,8 @@ enum nl80211_bss { NL80211_BSS_FREQUENCY_OFFSET, NL80211_BSS_MLO_LINK_ID, NL80211_BSS_MLD_ADDR, + NL80211_BSS_USE_FOR, + NL80211_BSS_CANNOT_USE_REASONS, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -5338,7 +5556,7 @@ enum nl80211_tx_rate_setting { * (%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE). * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but * per peer instead. - * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates + * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if set indicates * that the new configuration overrides all previous peer * configurations, otherwise previous peer specific configurations * should be left untouched. @@ -5539,6 +5757,8 @@ struct nl80211_pattern_support { * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one * frequency, it means that the match occurred in more than one * channel. + * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only. + * Wake up happened due to unprotected deauth or disassoc frame in MFP. * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number * @@ -5566,6 +5786,7 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, NL80211_WOWLAN_TRIG_NET_DETECT, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS, + NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC, /* keep last */ NUM_NL80211_WOWLAN_TRIG, @@ -5721,7 +5942,7 @@ enum nl80211_attr_coalesce_rule { /** * enum nl80211_coalesce_condition - coalesce rule conditions - * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns + * @NL80211_COALESCE_CONDITION_MATCH: coalesce Rx packets when patterns * in a rule are matched. * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns * in a rule are not matched. @@ -5820,7 +6041,7 @@ enum nl80211_if_combination_attrs { * enum nl80211_plink_state - state of a mesh peer link finite state machine * * @NL80211_PLINK_LISTEN: initial state, considered the implicit - * state of non existent mesh peer links + * state of non-existent mesh peer links * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to * this mesh peer * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received @@ -5869,6 +6090,7 @@ enum plink_actions { #define NL80211_KEK_LEN 16 #define NL80211_KCK_EXT_LEN 24 #define NL80211_KEK_EXT_LEN 32 +#define NL80211_KCK_EXT_LEN_32 32 #define NL80211_REPLAY_CTR_LEN 8 /** @@ -6112,7 +6334,7 @@ enum nl80211_feature_flags { * request to use RRM (see %NL80211_ATTR_USE_RRM) with * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set * the ASSOC_REQ_USE_RRM flag in the association request even if - * NL80211_FEATURE_QUIET is not advertized. + * NL80211_FEATURE_QUIET is not advertised. * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air * sniffer which means that it can be configured to hear packets from * certain groups which can be configured by the @@ -6124,13 +6346,15 @@ enum nl80211_feature_flags { * the BSS that the interface that requested the scan is connected to * (if available). * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the - * time the last beacon/probe was received. The time is the TSF of the - * BSS that the interface that requested the scan is connected to - * (if available). + * time the last beacon/probe was received. For a non-MLO connection, the + * time is the TSF of the BSS that the interface that requested the scan is + * connected to (if available). For an MLO connection, the time is the TSF + * of the BSS corresponding with link ID specified in the scan request (if + * specified). * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of * channel dwell time. * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate - * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate. + * configuration (AP/mesh), supporting a legacy (non-HT/VHT) rate. * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate * configuration (AP/mesh) with HT rates. * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate @@ -6204,8 +6428,7 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching * (set/del PMKSA operations) in AP mode. * - * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports - * filtering of sched scan results using band specific RSSI thresholds. + * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete * * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power * to a station. @@ -6294,6 +6517,31 @@ enum nl80211_feature_flags { * might apply, e.g. no scans in progress, no offchannel operations * in progress, and no active connections. * + * @NL80211_EXT_FEATURE_PUNCT: Driver supports preamble puncturing in AP mode. + * + * @NL80211_EXT_FEATURE_SECURE_NAN: Device supports NAN Pairing which enables + * authentication, data encryption and message integrity. + * + * @NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA: Device supports randomized TA + * in authentication and deauthentication frames sent to unassociated peer + * using @NL80211_CMD_FRAME. + * + * @NL80211_EXT_FEATURE_OWE_OFFLOAD: Driver/Device wants to do OWE DH IE + * handling in station mode. + * + * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE + * handling in AP mode. + * + * @NL80211_EXT_FEATURE_DFS_CONCURRENT: The device supports peer-to-peer or + * ad hoc operation on DFS channels under the control of a concurrent + * DFS master on the same channel as described in FCC-594280 D01 + * (Section B.3). This, for example, allows P2P GO and P2P clients to + * operate on DFS channels as long as there's a concurrent BSS connection. + * + * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP + * (signaling and payload protected) A-MSDUs and this shall be advertised + * in the RSNXE. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6335,7 +6583,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS, NL80211_EXT_FEATURE_AP_PMKSA_CACHING, - NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, + NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */ NL80211_EXT_FEATURE_EXT_KEY_ID, NL80211_EXT_FEATURE_STA_TX_PWR, NL80211_EXT_FEATURE_SAE_OFFLOAD, @@ -6362,6 +6610,13 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD, NL80211_EXT_FEATURE_RADAR_BACKGROUND, NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE, + NL80211_EXT_FEATURE_PUNCT, + NL80211_EXT_FEATURE_SECURE_NAN, + NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA, + NL80211_EXT_FEATURE_OWE_OFFLOAD, + NL80211_EXT_FEATURE_OWE_OFFLOAD_AP, + NL80211_EXT_FEATURE_DFS_CONCURRENT, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -6446,7 +6701,7 @@ enum nl80211_timeout_reason { * request parameters IE in the probe request * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at - * rate of at least 5.5M. In case non OCE AP is discovered in the channel, + * rate of at least 5.5M. In case non-OCE AP is discovered in the channel, * only the first probe req in the channel will be sent in high rate. * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request * tx deferral (dot11FILSProbeDelay shall be set to 15ms) @@ -6476,8 +6731,16 @@ enum nl80211_timeout_reason { * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with * %NL80211_ATTR_SCAN_FREQ_KHZ. This also means * %NL80211_ATTR_SCAN_FREQUENCIES will not be included. - * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for colocated APs reported by - * 2.4/5 GHz APs + * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for collocated APs reported by + * 2.4/5 GHz APs. When the flag is set, the scan logic will use the + * information from the RNR element found in beacons/probe responses + * received on the 2.4/5 GHz channels to actively scan only the 6GHz + * channels on which APs are expected to be found. Note that when not set, + * the scan logic would scan all 6GHz channels, but since transmission of + * probe requests on non-PSC channels is limited, it is highly likely that + * these channels would passively be scanned. Also note that when the flag + * is set, in addition to the colocated APs, PSC channels would also be + * scanned if the user space has asked for it. */ enum nl80211_scan_flags { NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, @@ -6806,7 +7069,7 @@ enum nl80211_nan_func_term_reason { * The instance ID for the follow up Service Discovery Frame. This is u8. * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type * is follow up. This is a u8. - * The requestor instance ID for the follow up Service Discovery Frame. + * The requester instance ID for the follow up Service Discovery Frame. * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the * follow up Service Discovery Frame. This is a binary attribute. * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a @@ -7196,7 +7459,7 @@ enum nl80211_peer_measurement_attrs { * @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if * trigger based ranging measurement is supported * @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating - * if non trigger based ranging measurement is supported + * if non-trigger-based ranging measurement is supported * * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number @@ -7250,7 +7513,7 @@ enum nl80211_peer_measurement_ftm_capa { * if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based * ranging will be used. - * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based + * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non-trigger-based * ranging measurement (flag) * This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are * mutually exclusive. @@ -7328,7 +7591,7 @@ enum nl80211_peer_measurement_ftm_failure_reasons { * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames * transmitted (u32, optional) * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames - * that were acknowleged (u32, optional) + * that were acknowledged (u32, optional) * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the * busy peer (u32, seconds) * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent @@ -7489,7 +7752,7 @@ enum nl80211_iftype_akm_attributes { * @NL80211_FILS_DISCOVERY_ATTR_INT_MIN: Minimum packet interval (u32, TU). * Allowed range: 0..10000 (TU = Time Unit) * @NL80211_FILS_DISCOVERY_ATTR_INT_MAX: Maximum packet interval (u32, TU). - * Allowed range: 0..10000 (TU = Time Unit) + * Allowed range: 0..10000 (TU = Time Unit). If set to 0, the feature is disabled. * @NL80211_FILS_DISCOVERY_ATTR_TMPL: Template data for FILS discovery action * frame including the headers. * @@ -7522,7 +7785,8 @@ enum nl80211_fils_discovery_attributes { * * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT: Maximum packet interval (u32, TU). * Allowed range: 0..20 (TU = Time Unit). IEEE P802.11ax/D6.0 - * 26.17.2.3.2 (AP behavior for fast passive scanning). + * 26.17.2.3.2 (AP behavior for fast passive scanning). If set to 0, the feature is + * disabled. * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL: Unsolicited broadcast probe response * frame template (binary). * diff --git a/src/basic/linux/pkt_sched.h b/src/basic/linux/pkt_sched.h index 000eec1..a3cd0c2 100644 --- a/src/basic/linux/pkt_sched.h +++ b/src/basic/linux/pkt_sched.h @@ -477,115 +477,6 @@ enum { #define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) - -/* CBQ section */ - -#define TC_CBQ_MAXPRIO 8 -#define TC_CBQ_MAXLEVEL 8 -#define TC_CBQ_DEF_EWMA 5 - -struct tc_cbq_lssopt { - unsigned char change; - unsigned char flags; -#define TCF_CBQ_LSS_BOUNDED 1 -#define TCF_CBQ_LSS_ISOLATED 2 - unsigned char ewma_log; - unsigned char level; -#define TCF_CBQ_LSS_FLAGS 1 -#define TCF_CBQ_LSS_EWMA 2 -#define TCF_CBQ_LSS_MAXIDLE 4 -#define TCF_CBQ_LSS_MINIDLE 8 -#define TCF_CBQ_LSS_OFFTIME 0x10 -#define TCF_CBQ_LSS_AVPKT 0x20 - __u32 maxidle; - __u32 minidle; - __u32 offtime; - __u32 avpkt; -}; - -struct tc_cbq_wrropt { - unsigned char flags; - unsigned char priority; - unsigned char cpriority; - unsigned char __reserved; - __u32 allot; - __u32 weight; -}; - -struct tc_cbq_ovl { - unsigned char strategy; -#define TC_CBQ_OVL_CLASSIC 0 -#define TC_CBQ_OVL_DELAY 1 -#define TC_CBQ_OVL_LOWPRIO 2 -#define TC_CBQ_OVL_DROP 3 -#define TC_CBQ_OVL_RCLASSIC 4 - unsigned char priority2; - __u16 pad; - __u32 penalty; -}; - -struct tc_cbq_police { - unsigned char police; - unsigned char __res1; - unsigned short __res2; -}; - -struct tc_cbq_fopt { - __u32 split; - __u32 defmap; - __u32 defchange; -}; - -struct tc_cbq_xstats { - __u32 borrows; - __u32 overactions; - __s32 avgidle; - __s32 undertime; -}; - -enum { - TCA_CBQ_UNSPEC, - TCA_CBQ_LSSOPT, - TCA_CBQ_WRROPT, - TCA_CBQ_FOPT, - TCA_CBQ_OVL_STRATEGY, - TCA_CBQ_RATE, - TCA_CBQ_RTAB, - TCA_CBQ_POLICE, - __TCA_CBQ_MAX, -}; - -#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) - -/* dsmark section */ - -enum { - TCA_DSMARK_UNSPEC, - TCA_DSMARK_INDICES, - TCA_DSMARK_DEFAULT_INDEX, - TCA_DSMARK_SET_TC_INDEX, - TCA_DSMARK_MASK, - TCA_DSMARK_VALUE, - __TCA_DSMARK_MAX, -}; - -#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) - -/* ATM section */ - -enum { - TCA_ATM_UNSPEC, - TCA_ATM_FD, /* file/socket descriptor */ - TCA_ATM_PTR, /* pointer to descriptor - later */ - TCA_ATM_HDR, /* LL header */ - TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ - TCA_ATM_ADDR, /* PVC address (for output only) */ - TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ - __TCA_ATM_MAX, -}; - -#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) - /* Network emulator */ enum { @@ -603,6 +494,7 @@ enum { TCA_NETEM_JITTER64, TCA_NETEM_SLOT, TCA_NETEM_SLOT_DIST, + TCA_NETEM_PRNG_SEED, __TCA_NETEM_MAX, }; @@ -719,6 +611,11 @@ enum { #define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1) +enum { + TC_FP_EXPRESS = 1, + TC_FP_PREEMPTIBLE = 2, +}; + struct tc_mqprio_qopt { __u8 num_tc; __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; @@ -733,11 +630,22 @@ struct tc_mqprio_qopt { #define TC_MQPRIO_F_MAX_RATE 0x8 enum { + TCA_MQPRIO_TC_ENTRY_UNSPEC, + TCA_MQPRIO_TC_ENTRY_INDEX, /* u32 */ + TCA_MQPRIO_TC_ENTRY_FP, /* u32 */ + + /* add new constants above here */ + __TCA_MQPRIO_TC_ENTRY_CNT, + TCA_MQPRIO_TC_ENTRY_MAX = (__TCA_MQPRIO_TC_ENTRY_CNT - 1) +}; + +enum { TCA_MQPRIO_UNSPEC, TCA_MQPRIO_MODE, TCA_MQPRIO_SHAPER, TCA_MQPRIO_MIN_RATE64, TCA_MQPRIO_MAX_RATE64, + TCA_MQPRIO_TC_ENTRY, __TCA_MQPRIO_MAX, }; @@ -924,15 +832,22 @@ enum { TCA_FQ_HORIZON_DROP, /* drop packets beyond horizon, or cap their EDT */ + TCA_FQ_PRIOMAP, /* prio2band */ + + TCA_FQ_WEIGHTS, /* Weights for each band */ + __TCA_FQ_MAX }; #define TCA_FQ_MAX (__TCA_FQ_MAX - 1) +#define FQ_BANDS 3 +#define FQ_MIN_WEIGHT 16384 + struct tc_fq_qd_stats { __u64 gc_flows; - __u64 highprio_packets; - __u64 tcp_retrans; + __u64 highprio_packets; /* obsolete */ + __u64 tcp_retrans; /* obsolete */ __u64 throttled; __u64 flows_plimit; __u64 pkts_too_long; @@ -945,6 +860,10 @@ struct tc_fq_qd_stats { __u64 ce_mark; /* packets above ce_threshold */ __u64 horizon_drops; __u64 horizon_caps; + __u64 fastpath_packets; + __u64 band_drops[FQ_BANDS]; + __u32 band_pkt_count[FQ_BANDS]; + __u32 pad; }; /* Heavy-Hitter Filter */ @@ -1236,6 +1155,7 @@ enum { TCA_TAPRIO_TC_ENTRY_UNSPEC, TCA_TAPRIO_TC_ENTRY_INDEX, /* u32 */ TCA_TAPRIO_TC_ENTRY_MAX_SDU, /* u32 */ + TCA_TAPRIO_TC_ENTRY_FP, /* u32 */ /* add new constants above here */ __TCA_TAPRIO_TC_ENTRY_CNT, @@ -1243,6 +1163,16 @@ enum { }; enum { + TCA_TAPRIO_OFFLOAD_STATS_PAD = 1, /* u64 */ + TCA_TAPRIO_OFFLOAD_STATS_WINDOW_DROPS, /* u64 */ + TCA_TAPRIO_OFFLOAD_STATS_TX_OVERRUNS, /* u64 */ + + /* add new constants above here */ + __TCA_TAPRIO_OFFLOAD_STATS_CNT, + TCA_TAPRIO_OFFLOAD_STATS_MAX = (__TCA_TAPRIO_OFFLOAD_STATS_CNT - 1) +}; + +enum { TCA_TAPRIO_ATTR_UNSPEC, TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */ TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST, /* nested of entry */ diff --git a/src/basic/linux/rtnetlink.h b/src/basic/linux/rtnetlink.h index eb2747d..3b687d2 100644 --- a/src/basic/linux/rtnetlink.h +++ b/src/basic/linux/rtnetlink.h @@ -502,13 +502,17 @@ enum { #define RTAX_MAX (__RTAX_MAX - 1) -#define RTAX_FEATURE_ECN (1 << 0) -#define RTAX_FEATURE_SACK (1 << 1) -#define RTAX_FEATURE_TIMESTAMP (1 << 2) -#define RTAX_FEATURE_ALLFRAG (1 << 3) - -#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | \ - RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG) +#define RTAX_FEATURE_ECN (1 << 0) +#define RTAX_FEATURE_SACK (1 << 1) /* unused */ +#define RTAX_FEATURE_TIMESTAMP (1 << 2) /* unused */ +#define RTAX_FEATURE_ALLFRAG (1 << 3) /* unused */ +#define RTAX_FEATURE_TCP_USEC_TS (1 << 4) + +#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | \ + RTAX_FEATURE_SACK | \ + RTAX_FEATURE_TIMESTAMP | \ + RTAX_FEATURE_ALLFRAG | \ + RTAX_FEATURE_TCP_USEC_TS) struct rta_session { __u8 proto; @@ -635,6 +639,7 @@ enum { TCA_INGRESS_BLOCK, TCA_EGRESS_BLOCK, TCA_DUMP_FLAGS, + TCA_EXT_WARN_MSG, __TCA_MAX }; @@ -788,6 +793,7 @@ enum { TCA_ROOT_FLAGS, TCA_ROOT_COUNT, TCA_ROOT_TIME_DELTA, /* in msecs */ + TCA_ROOT_EXT_WARN_MSG, __TCA_ROOT_MAX, #define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) }; diff --git a/src/basic/linux/stddef.h b/src/basic/linux/stddef.h index 1a73963..b888a83 100644 --- a/src/basic/linux/stddef.h +++ b/src/basic/linux/stddef.h @@ -26,8 +26,13 @@ union { \ struct { MEMBERS } ATTRS; \ struct TAG { MEMBERS } ATTRS NAME; \ - } + } ATTRS +#ifdef __cplusplus +/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */ +#define __DECLARE_FLEX_ARRAY(T, member) \ + T member[0] +#else /** * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union * @@ -44,3 +49,9 @@ TYPE NAME[]; \ } #endif + +#ifndef __counted_by +#define __counted_by(m) +#endif + +#endif /* _UAPI_LINUX_STDDEF_H */ diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index d3fef01..2356527 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -221,7 +221,7 @@ int get_locales(char ***ret) { locales = set_free(locales); r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES"); - if (r == -ENXIO || r == 0) { + if (IN_SET(r, -ENXIO, 0)) { char **a, **b; /* Filter out non-UTF-8 locales, because it's 2019, by default */ @@ -260,7 +260,10 @@ bool locale_is_valid(const char *name) { if (!filename_is_valid(name)) return false; - if (!string_is_safe(name)) + /* Locales look like: ll_CC.ENC@variant, where ll and CC are alphabetic, ENC is alphanumeric with + * dashes, and variant seems to be alphabetic. + * See: https://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html */ + if (!in_charset(name, ALPHANUMERICAL "_.-@")) return false; return true; @@ -292,7 +295,7 @@ bool is_locale_utf8(void) { if (cached_answer >= 0) goto out; - r = getenv_bool_secure("SYSTEMD_UTF8"); + r = secure_getenv_bool("SYSTEMD_UTF8"); if (r >= 0) { cached_answer = r; goto out; diff --git a/src/basic/lock-util.c b/src/basic/lock-util.c index 7bffe85..aef395d 100644 --- a/src/basic/lock-util.c +++ b/src/basic/lock-util.c @@ -139,7 +139,14 @@ static int fcntl_lock(int fd, int operation, bool ofd) { .l_len = 0, })); - if (r == -EACCES) /* Treat EACCESS/EAGAIN the same as per man page. */ + /* If we are doing non-blocking operations, treat EACCES/EAGAIN the same as per man page. But if + * not, propagate EACCES back, as it will likely be due to an LSM denying the operation (for example + * LXC with AppArmor when running on kernel < 6.2), and in some cases we want to gracefully + * fallback (e.g.: PrivateNetwork=yes). As per documentation, it's only the non-blocking operation + * F_SETLK that might return EACCES on some platforms (although the Linux implementation doesn't + * seem to), as F_SETLKW and F_OFD_SETLKW block so this is not an issue, and F_OFD_SETLK is documented + * to only return EAGAIN if the lock is already held. */ + if ((operation & LOCK_NB) && r == -EACCES) r = -EAGAIN; return r; diff --git a/src/basic/lock-util.h b/src/basic/lock-util.h index 91b332f..8fb4757 100644 --- a/src/basic/lock-util.h +++ b/src/basic/lock-util.h @@ -17,7 +17,7 @@ static inline int make_lock_file(const char *p, int operation, LockFile *ret) { int make_lock_file_for(const char *p, int operation, LockFile *ret); void release_lock_file(LockFile *f); -#define LOCK_FILE_INIT { .dir_fd = -EBADF, .fd = -EBADF } +#define LOCK_FILE_INIT (LockFile) { .dir_fd = -EBADF, .fd = -EBADF } /* POSIX locks with the same interface as flock(). */ int posix_lock(int fd, int operation); diff --git a/src/basic/log.c b/src/basic/log.c index 7a44300..13ad19a 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -49,6 +49,12 @@ static void *log_syntax_callback_userdata = NULL; static LogTarget log_target = LOG_TARGET_CONSOLE; static int log_max_level = LOG_INFO; +static int log_target_max_level[] = { + [LOG_TARGET_CONSOLE] = INT_MAX, + [LOG_TARGET_KMSG] = INT_MAX, + [LOG_TARGET_SYSLOG] = INT_MAX, + [LOG_TARGET_JOURNAL] = INT_MAX, +}; static int log_facility = LOG_DAEMON; static bool ratelimit_kmsg = true; @@ -69,6 +75,7 @@ static bool upgrade_syslog_to_journal = false; static bool always_reopen_console = false; static bool open_when_needed = false; static bool prohibit_ipc = false; +static bool assert_return_is_critical = BUILD_MODE_DEVELOPER; /* Akin to glibc's __abort_msg; which is private and we hence cannot * use here. */ @@ -249,7 +256,7 @@ fail: return r; } -static bool stderr_is_journal(void) { +bool stderr_is_journal(void) { _cleanup_free_ char *w = NULL; const char *e; uint64_t dev, ino; @@ -389,7 +396,7 @@ void log_forget_fds(void) { } void log_set_max_level(int level) { - assert(level == LOG_NULL || (level & LOG_PRIMASK) == level); + assert(level == LOG_NULL || LOG_PRI(level) == level); log_max_level = level; @@ -414,7 +421,7 @@ static bool check_console_fd_is_tty(void) { return false; if (console_fd_is_tty < 0) - console_fd_is_tty = isatty(console_fd) > 0; + console_fd_is_tty = isatty_safe(console_fd); return console_fd_is_tty; } @@ -443,6 +450,9 @@ static int write_to_console( if (dumb < 0) dumb = getenv_terminal_is_dumb(); + if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_CONSOLE]) + return 0; + if (log_target == LOG_TARGET_CONSOLE_PREFIXED) { xsprintf(prefix, "<%i>", level); iovec[n++] = IOVEC_MAKE_STRING(prefix); @@ -528,6 +538,9 @@ static int write_to_syslog( if (syslog_fd < 0) return 0; + if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_SYSLOG]) + return 0; + xsprintf(header_priority, "<%i>", level); t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC); @@ -597,6 +610,9 @@ static int write_to_kmsg( if (kmsg_fd < 0) return 0; + if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_KMSG]) + return 0; + if (ratelimit_kmsg && !ratelimit_below(&ratelimit)) { if (ratelimit_num_dropped(&ratelimit) > 1) return 0; @@ -724,6 +740,9 @@ static int write_to_journal( if (journal_fd < 0) return 0; + if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_JOURNAL]) + return 0; + iovec_len = MIN(6 + _log_context_num_fields * 2, IOVEC_MAX); iovec = newa(struct iovec, iovec_len); @@ -769,7 +788,7 @@ int log_dispatch_internal( return -ERRNO_VALUE(error); /* Patch in LOG_DAEMON facility if necessary */ - if ((level & LOG_FACMASK) == 0) + if (LOG_FAC(level) == 0) level |= log_facility; if (open_when_needed) @@ -987,9 +1006,13 @@ void log_assert_failed_return( const char *file, int line, const char *func) { + + if (assert_return_is_critical) + log_assert_failed(text, file, line, func); + PROTECT_ERRNO; log_assert(LOG_DEBUG, text, file, line, func, - "Assertion '%s' failed at %s:%u, function %s(). Ignoring."); + "Assertion '%s' failed at %s:%u, function %s(), ignoring."); } int log_oom_internal(int level, const char *file, int line, const char *func) { @@ -1054,7 +1077,7 @@ int log_struct_internal( log_target == LOG_TARGET_NULL) return -ERRNO_VALUE(error); - if ((level & LOG_FACMASK) == 0) + if (LOG_FAC(level) == 0) level |= log_facility; if (IN_SET(log_target, @@ -1157,7 +1180,7 @@ int log_struct_iovec_internal( log_target == LOG_TARGET_NULL) return -ERRNO_VALUE(error); - if ((level & LOG_FACMASK) == 0) + if (LOG_FAC(level) == 0) level |= log_facility; if (IN_SET(log_target, LOG_TARGET_AUTO, @@ -1219,11 +1242,74 @@ int log_set_target_from_string(const char *e) { int log_set_max_level_from_string(const char *e) { int r; - r = log_level_from_string(e); + for (;;) { + _cleanup_free_ char *word = NULL, *prefix = NULL; + LogTarget target; + const char *colon; + + r = extract_first_word(&e, &word, ",", 0); + if (r < 0) + return r; + if (r == 0) + break; + + colon = strchr(word, ':'); + if (!colon) { + r = log_level_from_string(word); + if (r < 0) + return r; + + log_set_max_level(r); + continue; + } + + prefix = strndup(word, colon - word); + if (!prefix) + return -ENOMEM; + + target = log_target_from_string(prefix); + if (target < 0) + return target; + + if (target >= _LOG_TARGET_SINGLE_MAX) + return -EINVAL; + + r = log_level_from_string(colon + 1); + if (r < 0) + return r; + + log_target_max_level[target] = r; + } + + return 0; +} + +int log_max_levels_to_string(int level, char **ret) { + _cleanup_free_ char *s = NULL; + int r; + + assert(ret); + + r = log_level_to_string_alloc(level, &s); if (r < 0) return r; - log_set_max_level(r); + for (LogTarget target = 0; target < _LOG_TARGET_SINGLE_MAX; target++) { + _cleanup_free_ char *l = NULL; + + if (log_target_max_level[target] == INT_MAX) + continue; + + r = log_level_to_string_alloc(log_target_max_level[target], &l); + if (r < 0) + return r; + + r = strextendf_with_separator(&s, ",", "%s:%s", log_target_to_string(target), l); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(s); return 0; } @@ -1238,6 +1324,14 @@ static int log_set_ratelimit_kmsg_from_string(const char *e) { return 0; } +void log_set_assert_return_is_critical(bool b) { + assert_return_is_critical = b; +} + +bool log_get_assert_return_is_critical(void) { + return assert_return_is_critical; +} + static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { /* @@ -1258,7 +1352,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; if (log_set_target_from_string(value) < 0) - log_warning("Failed to parse log target '%s'. Ignoring.", value); + log_warning("Failed to parse log target '%s', ignoring.", value); } else if (proc_cmdline_key_streq(key, "systemd.log_level")) { @@ -1266,32 +1360,32 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; if (log_set_max_level_from_string(value) < 0) - log_warning("Failed to parse log level '%s'. Ignoring.", value); + log_warning("Failed to parse log level setting '%s', ignoring.", value); } else if (proc_cmdline_key_streq(key, "systemd.log_color")) { if (log_show_color_from_string(value ?: "1") < 0) - log_warning("Failed to parse log color setting '%s'. Ignoring.", value); + log_warning("Failed to parse log color setting '%s', ignoring.", value); } else if (proc_cmdline_key_streq(key, "systemd.log_location")) { if (log_show_location_from_string(value ?: "1") < 0) - log_warning("Failed to parse log location setting '%s'. Ignoring.", value); + log_warning("Failed to parse log location setting '%s', ignoring.", value); } else if (proc_cmdline_key_streq(key, "systemd.log_tid")) { if (log_show_tid_from_string(value ?: "1") < 0) - log_warning("Failed to parse log tid setting '%s'. Ignoring.", value); + log_warning("Failed to parse log tid setting '%s', ignoring.", value); } else if (proc_cmdline_key_streq(key, "systemd.log_time")) { if (log_show_time_from_string(value ?: "1") < 0) - log_warning("Failed to parse log time setting '%s'. Ignoring.", value); + log_warning("Failed to parse log time setting '%s', ignoring.", value); } else if (proc_cmdline_key_streq(key, "systemd.log_ratelimit_kmsg")) { if (log_set_ratelimit_kmsg_from_string(value ?: "1") < 0) - log_warning("Failed to parse log ratelimit kmsg boolean '%s'. Ignoring.", value); + log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", value); } return 0; @@ -1311,31 +1405,31 @@ void log_parse_environment_variables(void) { e = getenv("SYSTEMD_LOG_TARGET"); if (e && log_set_target_from_string(e) < 0) - log_warning("Failed to parse log target '%s'. Ignoring.", e); + log_warning("Failed to parse log target '%s', ignoring.", e); e = getenv("SYSTEMD_LOG_LEVEL"); if (e && log_set_max_level_from_string(e) < 0) - log_warning("Failed to parse log level '%s'. Ignoring.", e); + log_warning("Failed to parse log level '%s', ignoring.", e); e = getenv("SYSTEMD_LOG_COLOR"); if (e && log_show_color_from_string(e) < 0) - log_warning("Failed to parse log color '%s'. Ignoring.", e); + log_warning("Failed to parse log color '%s', ignoring.", e); e = getenv("SYSTEMD_LOG_LOCATION"); if (e && log_show_location_from_string(e) < 0) - log_warning("Failed to parse log location '%s'. Ignoring.", e); + log_warning("Failed to parse log location '%s', ignoring.", e); e = getenv("SYSTEMD_LOG_TIME"); if (e && log_show_time_from_string(e) < 0) - log_warning("Failed to parse log time '%s'. Ignoring.", e); + log_warning("Failed to parse log time '%s', ignoring.", e); e = getenv("SYSTEMD_LOG_TID"); if (e && log_show_tid_from_string(e) < 0) - log_warning("Failed to parse log tid '%s'. Ignoring.", e); + log_warning("Failed to parse log tid '%s', ignoring.", e); e = getenv("SYSTEMD_LOG_RATELIMIT_KMSG"); if (e && log_set_ratelimit_kmsg_from_string(e) < 0) - log_warning("Failed to parse log ratelimit kmsg boolean '%s'. Ignoring.", e); + log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", e); } void log_parse_environment(void) { @@ -1667,7 +1761,7 @@ bool log_context_enabled(void) { if (saved_log_context_enabled >= 0) return saved_log_context_enabled; - r = getenv_bool_secure("SYSTEMD_ENABLE_LOG_CONTEXT"); + r = secure_getenv_bool("SYSTEMD_ENABLE_LOG_CONTEXT"); if (r < 0 && r != -ENXIO) log_debug_errno(r, "Failed to parse $SYSTEMD_ENABLE_LOG_CONTEXT, ignoring: %m"); diff --git a/src/basic/log.h b/src/basic/log.h index 9008d47..726f035 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -18,20 +18,21 @@ struct signalfd_siginfo; typedef enum LogTarget{ LOG_TARGET_CONSOLE, - LOG_TARGET_CONSOLE_PREFIXED, LOG_TARGET_KMSG, LOG_TARGET_JOURNAL, - LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_SYSLOG, + LOG_TARGET_CONSOLE_PREFIXED, + LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */ LOG_TARGET_NULL, - _LOG_TARGET_MAX, + _LOG_TARGET_SINGLE_MAX = LOG_TARGET_SYSLOG + 1, + _LOG_TARGET_MAX = LOG_TARGET_NULL + 1, _LOG_TARGET_INVALID = -EINVAL, } LogTarget; /* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be - * used a regular log level. */ + * used as a regular log level. */ #define LOG_NULL (LOG_EMERG - 1) /* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */ @@ -59,6 +60,7 @@ void log_settle_target(void); void log_set_max_level(int level); int log_set_max_level_from_string(const char *e); int log_get_max_level(void) _pure_; +int log_max_levels_to_string(int level, char **ret); void log_set_facility(int facility); @@ -83,6 +85,7 @@ int log_show_tid_from_string(const char *e); assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1); #define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1]) +bool stderr_is_journal(void); int log_open(void); void log_close(void); void log_forget_fds(void); @@ -331,6 +334,9 @@ void log_set_open_when_needed(bool b); * stderr, the console or kmsg */ void log_set_prohibit_ipc(bool b); +void log_set_assert_return_is_critical(bool b); +bool log_get_assert_return_is_critical(void) _pure_; + int log_dup_console(void); int log_syntax_internal( @@ -380,7 +386,7 @@ typedef struct LogRateLimit { RateLimit ratelimit; } LogRateLimit; -#define log_ratelimit_internal(_level, _error, _ratelimit, _format, _file, _line, _func, ...) \ +#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...) \ ({ \ int _log_ratelimit_error = (_error); \ int _log_ratelimit_level = (_level); \ @@ -404,7 +410,7 @@ typedef struct LogRateLimit { ({ \ int _level = (level), _e = (error); \ _e = (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_ratelimit_internal(_level, _e, _ratelimit, format, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__) \ + ? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \ : -ERRNO_VALUE(_e); \ _e < 0 ? _e : -ESTRPIPE; \ }) diff --git a/src/basic/macro.h b/src/basic/macro.h index d63aa81..19d5039 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -266,12 +266,6 @@ static inline int __coverity_check_and_return__(int condition) { /* Pointers range from NULL to POINTER_MAX */ #define POINTER_MAX ((void*) UINTPTR_MAX) -/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses POINTER_MAX as internal marker for EOL. */ -#define FOREACH_POINTER(p, x, ...) \ - for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, POINTER_MAX }; \ - p != (typeof(p)) POINTER_MAX; \ - p = *(++_l)) - #define _FOREACH_ARRAY(i, array, num, m, end) \ for (typeof(array[0]) *i = (array), *end = ({ \ typeof(num) m = (num); \ @@ -281,6 +275,9 @@ static inline int __coverity_check_and_return__(int condition) { #define FOREACH_ARRAY(i, array, num) \ _FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ)) +#define FOREACH_ELEMENT(i, array) \ + FOREACH_ARRAY(i, array, ELEMENTSOF(array)) + #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \ scope type *name##_ref(type *p) { \ if (!p) \ @@ -380,13 +377,26 @@ assert_cc(sizeof(dummy_t) == 0); _q && _q > (base) ? &_q[-1] : NULL; \ }) -/* Iterate through each variadic arg. All must be the same type as 'entry' or must be implicitly +/* Iterate through each argument passed. All must be the same type as 'entry' or must be implicitly * convertible. The iteration variable 'entry' must already be defined. */ -#define VA_ARGS_FOREACH(entry, ...) \ - _VA_ARGS_FOREACH(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), UNIQ_T(_va_sentinel_, UNIQ), ##__VA_ARGS__) -#define _VA_ARGS_FOREACH(entry, _entries_, _current_, _va_sentinel_, ...) \ +#define FOREACH_ARGUMENT(entry, ...) \ + _FOREACH_ARGUMENT(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), UNIQ_T(_va_sentinel_, UNIQ), ##__VA_ARGS__) +#define _FOREACH_ARGUMENT(entry, _entries_, _current_, _va_sentinel_, ...) \ for (typeof(entry) _va_sentinel_[1] = {}, _entries_[] = { __VA_ARGS__ __VA_OPT__(,) _va_sentinel_[0] }, *_current_ = _entries_; \ ((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \ _current_++) +#define DECIMAL_STR_FMT(x) _Generic((x), \ + char: "%c", \ + bool: "%d", \ + unsigned char: "%d", \ + short: "%hd", \ + unsigned short: "%hu", \ + int: "%d", \ + unsigned: "%u", \ + long: "%ld", \ + unsigned long: "%lu", \ + long long: "%lld", \ + unsigned long long: "%llu") + #include "log.h" diff --git a/src/basic/memory-util.c b/src/basic/memory-util.c index fcedae2..ed6024f 100644 --- a/src/basic/memory-util.c +++ b/src/basic/memory-util.c @@ -39,3 +39,19 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) { /* Now we know first 16 bytes match, memcmp() with self. */ return memcmp(data, p + 16, length) == 0; } + +void *memdup_reverse(const void *mem, size_t size) { + assert(mem); + assert(size != 0); + + void *p = malloc(size); + if (!p) + return NULL; + + uint8_t *p_dst = p; + const uint8_t *p_src = mem; + for (size_t i = 0, k = size; i < size; i++, k--) + p_dst[i] = p_src[k-1]; + + return p; +} diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h index 1179513..294aed6 100644 --- a/src/basic/memory-util.h +++ b/src/basic/memory-util.h @@ -107,3 +107,6 @@ static inline void erase_and_freep(void *p) { static inline void erase_char(char *p) { explicit_bzero_safe(p, sizeof(char)); } + +/* Makes a copy of the buffer with reversed order of bytes */ +void *memdup_reverse(const void *mem, size_t size); diff --git a/src/basic/meson.build b/src/basic/meson.build index 111253e..9a21457 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -10,16 +10,19 @@ basic_sources = files( 'audit-util.c', 'btrfs.c', 'build.c', + 'build-path.c', 'bus-label.c', 'cap-list.c', 'capability-util.c', 'cgroup-util.c', 'chase.c', 'chattr-util.c', + 'compress.c', 'conf-files.c', 'confidential-virt.c', 'devnum-util.c', 'dirent-util.c', + 'dlfcn-util.c', 'efivars.c', 'env-file.c', 'env-util.c', @@ -32,6 +35,7 @@ basic_sources = files( 'filesystems.c', 'format-util.c', 'fs-util.c', + 'gcrypt-util.c', 'glob-util.c', 'glyph-util.c', 'gunicode.c', @@ -53,6 +57,7 @@ basic_sources = files( 'lock-util.c', 'log.c', 'login-util.c', + 'keyring-util.c', 'memfd-util.c', 'memory-util.c', 'mempool.c', @@ -79,6 +84,7 @@ basic_sources = files( 'replace-var.c', 'rlimit-util.c', 'runtime-scope.c', + 'sha256.c', 'sigbus.c', 'signal-util.c', 'siphash24.c', @@ -96,7 +102,7 @@ basic_sources = files( 'terminal-util.c', 'time-util.c', 'tmpfile-util.c', - 'uid-alloc-range.c', + 'uid-classification.c', 'uid-range.c', 'unit-def.c', 'unit-file.c', @@ -229,8 +235,10 @@ run_target( ############################################################ -filesystem_includes = ['linux/magic.h', - 'linux/gfs2_ondisk.h'] +filesystem_includes = files( + 'linux/magic.h', + 'missing_magic.h', +) check_filesystems = find_program('check-filesystems.sh') r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false) @@ -272,45 +280,14 @@ libbasic = static_library( fundamental_sources, include_directories : basic_includes, dependencies : [libcap, + libdl, + libgcrypt_cflags, + liblz4_cflags, libm, librt, + libxz_cflags, + libzstd_cflags, threads, userspace], c_args : ['-fvisibility=default'], build_by_default : false) - -############################################################ - -basic_gcrypt_sources = files( - 'gcrypt-util.c', -) - -# A convenience library that is separate from libbasic to avoid -# unnecessary linking to libgcrypt. -libbasic_gcrypt = static_library( - 'basic-gcrypt', - basic_gcrypt_sources, - include_directories : basic_includes, - dependencies : [libgcrypt, - userspace], - c_args : ['-fvisibility=default'], - build_by_default : false) - -############################################################ - -basic_compress_sources = files( - 'compress.c', -) - -# A convenience library that is separate from libbasic to avoid unnecessary -# linking to the compression libraries. -libbasic_compress = static_library( - 'basic-compress', - basic_compress_sources, - include_directories : basic_includes, - dependencies : [liblz4, - libxz, - libzstd, - userspace], - c_args : ['-fvisibility=default'], - build_by_default : false) diff --git a/src/basic/missing_audit.h b/src/basic/missing_audit.h index 62e3c29..3f72acf 100644 --- a/src/basic/missing_audit.h +++ b/src/basic/missing_audit.h @@ -4,21 +4,31 @@ #include <linux/audit.h> #if HAVE_AUDIT -#include <libaudit.h> +# include <libaudit.h> #endif #ifndef AUDIT_SERVICE_START -#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */ +# define AUDIT_SERVICE_START 1130 /* Service (daemon) start */ +#else +assert_cc(AUDIT_SERVICE_START == 1130); #endif #ifndef AUDIT_SERVICE_STOP -#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */ +# define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */ +#else +assert_cc(AUDIT_SERVICE_STOP == 1131); #endif #ifndef MAX_AUDIT_MESSAGE_LENGTH -#define MAX_AUDIT_MESSAGE_LENGTH 8970 +# define MAX_AUDIT_MESSAGE_LENGTH 8970 +#else +assert_cc(MAX_AUDIT_MESSAGE_LENGTH == 8970); #endif +/* Note: we check for AUDIT_NLGRP_MAX because it's a define, but we actually + * need AUDIT_NLGRP_READLOG which is an enum. */ #ifndef AUDIT_NLGRP_MAX -#define AUDIT_NLGRP_READLOG 1 +# define AUDIT_NLGRP_READLOG 1 +#else +assert_cc(AUDIT_NLGRP_READLOG == 1); #endif diff --git a/src/basic/missing_capability.h b/src/basic/missing_capability.h index 5adda55..c1c63a6 100644 --- a/src/basic/missing_capability.h +++ b/src/basic/missing_capability.h @@ -6,21 +6,29 @@ /* 3a101b8de0d39403b2c7e5c23fd0b005668acf48 (3.16) */ #ifndef CAP_AUDIT_READ # define CAP_AUDIT_READ 37 +#else +assert_cc(CAP_AUDIT_READ == 37); #endif /* 980737282232b752bb14dab96d77665c15889c36 (5.8) */ #ifndef CAP_PERFMON # define CAP_PERFMON 38 +#else +assert_cc(CAP_PERFMON == 38); #endif /* a17b53c4a4b55ec322c132b6670743612229ee9c (5.8) */ #ifndef CAP_BPF # define CAP_BPF 39 +#else +assert_cc(CAP_BPF == 39); #endif /* 124ea650d3072b005457faed69909221c2905a1f (5.9) */ #ifndef CAP_CHECKPOINT_RESTORE # define CAP_CHECKPOINT_RESTORE 40 +#else +assert_cc(CAP_CHECKPOINT_RESTORE == 40); #endif #define SYSTEMD_CAP_LAST_CAP CAP_CHECKPOINT_RESTORE @@ -34,6 +42,7 @@ # undef CAP_LAST_CAP # endif #endif + #ifndef CAP_LAST_CAP # define CAP_LAST_CAP SYSTEMD_CAP_LAST_CAP #endif diff --git a/src/basic/missing_drm.h b/src/basic/missing_drm.h index 0dec591..e4ca56f 100644 --- a/src/basic/missing_drm.h +++ b/src/basic/missing_drm.h @@ -2,9 +2,9 @@ #pragma once #ifndef DRM_IOCTL_SET_MASTER -#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) +# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) #endif #ifndef DRM_IOCTL_DROP_MASTER -#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) +# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) #endif diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h index 9b03bba..d97b190 100644 --- a/src/basic/missing_fs.h +++ b/src/basic/missing_fs.h @@ -3,6 +3,8 @@ #include <linux/types.h> +#include "macro.h" + /* linux/fs.h */ #ifndef RENAME_NOREPLACE /* 0a7c3937a1f23f8cb5fc77ae01661e9968a51d0c (3.15) */ #define RENAME_NOREPLACE (1 << 0) @@ -28,43 +30,63 @@ struct file_clone_range { /* linux/fs.h or sys/mount.h */ #ifndef MS_MOVE -#define MS_MOVE 8192 +# define MS_MOVE 8192 +#else +assert_cc(MS_MOVE == 8192); #endif #ifndef MS_REC -#define MS_REC 16384 +# define MS_REC 16384 +#else +assert_cc(MS_REC == 16384); #endif #ifndef MS_PRIVATE -#define MS_PRIVATE (1<<18) +# define MS_PRIVATE (1<<18) +#else +assert_cc(MS_PRIVATE == (1<<18)); #endif #ifndef MS_SLAVE -#define MS_SLAVE (1<<19) +# define MS_SLAVE (1<<19) +#else +assert_cc(MS_SLAVE == (1<<19)); #endif #ifndef MS_SHARED -#define MS_SHARED (1<<20) +# define MS_SHARED (1<<20) +#else +assert_cc(MS_SHARED == (1<<20)); #endif #ifndef MS_RELATIME -#define MS_RELATIME (1<<21) +# define MS_RELATIME (1<<21) +#else +assert_cc(MS_RELATIME == (1<<21)); #endif #ifndef MS_KERNMOUNT -#define MS_KERNMOUNT (1<<22) +# define MS_KERNMOUNT (1<<22) +#else +assert_cc(MS_KERNMOUNT == (1<<22)); #endif #ifndef MS_I_VERSION -#define MS_I_VERSION (1<<23) +# define MS_I_VERSION (1<<23) +#else +assert_cc(MS_I_VERSION == (1<<23)); #endif #ifndef MS_STRICTATIME -#define MS_STRICTATIME (1<<24) +# define MS_STRICTATIME (1<<24) +#else +assert_cc(MS_STRICTATIME == (1 << 24)); #endif #ifndef MS_LAZYTIME -#define MS_LAZYTIME (1<<25) +# define MS_LAZYTIME (1<<25) +#else +assert_cc(MS_LAZYTIME == (1<<25)); #endif /* Not exposed yet. Defined at fs/ext4/ext4.h */ @@ -78,10 +100,19 @@ struct file_clone_range { #endif #ifndef FS_PROJINHERIT_FL -#define FS_PROJINHERIT_FL 0x20000000 +# define FS_PROJINHERIT_FL 0x20000000 +#else +assert_cc(FS_PROJINHERIT_FL == 0x20000000); #endif /* linux/fscrypt.h */ #ifndef FS_KEY_DESCRIPTOR_SIZE -#define FS_KEY_DESCRIPTOR_SIZE 8 +# define FS_KEY_DESCRIPTOR_SIZE 8 +#else +assert_cc(FS_KEY_DESCRIPTOR_SIZE == 8); +#endif + +/* linux/exportfs.h */ +#ifndef FILEID_KERNFS +#define FILEID_KERNFS 0xfe #endif diff --git a/src/basic/missing_input.h b/src/basic/missing_input.h index 6cf16ff..ee61bf9 100644 --- a/src/basic/missing_input.h +++ b/src/basic/missing_input.h @@ -4,9 +4,11 @@ #include <linux/input.h> #include <linux/types.h> +#include "macro.h" + /* linux@c7dc65737c9a607d3e6f8478659876074ad129b8 (3.12) */ #ifndef EVIOCREVOKE -#define EVIOCREVOKE _IOW('E', 0x91, int) +# define EVIOCREVOKE _IOW('E', 0x91, int) #endif /* linux@06a16293f71927f756dcf37558a79c0b05a91641 (4.4) */ @@ -17,29 +19,40 @@ struct input_mask { __u64 codes_ptr; }; -#define EVIOCGMASK _IOR('E', 0x92, struct input_mask) -#define EVIOCSMASK _IOW('E', 0x93, struct input_mask) +# define EVIOCGMASK _IOR('E', 0x92, struct input_mask) +# define EVIOCSMASK _IOW('E', 0x93, struct input_mask) #endif /* linux@7611392fe8ff95ecae528b01a815ae3d72ca6b95 (3.17) */ #ifndef INPUT_PROP_POINTING_STICK -#define INPUT_PROP_POINTING_STICK 0x05 +# define INPUT_PROP_POINTING_STICK 0x05 +#else +assert_cc(INPUT_PROP_POINTING_STICK == 0x05); #endif /* linux@500d4160abe9a2e88b12e319c13ae3ebd1e18108 (4.0) */ #ifndef INPUT_PROP_ACCELEROMETER -#define INPUT_PROP_ACCELEROMETER 0x06 +# define INPUT_PROP_ACCELEROMETER 0x06 +#else +assert_cc(INPUT_PROP_ACCELEROMETER == 0x06); #endif /* linux@d09bbfd2a8408a995419dff0d2ba906013cf4cc9 (3.11) */ #ifndef BTN_DPAD_UP -#define BTN_DPAD_UP 0x220 -#define BTN_DPAD_DOWN 0x221 -#define BTN_DPAD_LEFT 0x222 -#define BTN_DPAD_RIGHT 0x223 +# define BTN_DPAD_UP 0x220 +# define BTN_DPAD_DOWN 0x221 +# define BTN_DPAD_LEFT 0x222 +# define BTN_DPAD_RIGHT 0x223 +#else +assert_cc(BTN_DPAD_UP == 0x220); +assert_cc(BTN_DPAD_DOWN == 0x221); +assert_cc(BTN_DPAD_LEFT == 0x222); +assert_cc(BTN_DPAD_RIGHT == 0x223); #endif /* linux@358f24704f2f016af7d504b357cdf32606091d07 (3.13) */ #ifndef KEY_ALS_TOGGLE -#define KEY_ALS_TOGGLE 0x230 +# fine KEY_ALS_TOGGLE 0x230 +#else +assert_cc(KEY_ALS_TOGGLE == 0x230); #endif diff --git a/src/basic/missing_ioprio.h b/src/basic/missing_ioprio.h index 9cbd172..13ce792 100644 --- a/src/basic/missing_ioprio.h +++ b/src/basic/missing_ioprio.h @@ -1,49 +1,81 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include <sched.h> +#if HAVE_LINUX_IOPRIO_H +# include <linux/ioprio.h> +#endif + +#include "macro.h" /* Match values uses by the kernel internally, as no public header seems to exist. */ #ifndef IOPRIO_N_CLASSES # define IOPRIO_N_CLASSES 8 +#else +assert_cc(IOPRIO_N_CLASSES == 8); #endif #ifndef IOPRIO_BE_NR # define IOPRIO_BE_NR 8 +#else +assert_cc(IOPRIO_BE_NR == 8); #endif #ifndef IOPRIO_CLASS_NONE # define IOPRIO_CLASS_NONE 0 +#else +assert_cc(IOPRIO_CLASS_NONE == 0); #endif #ifndef IOPRIO_CLASS_RT # define IOPRIO_CLASS_RT 1 +#else +assert_cc(IOPRIO_CLASS_RT == 1); #endif #ifndef IOPRIO_CLASS_BE # define IOPRIO_CLASS_BE 2 +#else +assert_cc(IOPRIO_CLASS_BE == 2); #endif #ifndef IOPRIO_CLASS_IDLE # define IOPRIO_CLASS_IDLE 3 +#else +assert_cc(IOPRIO_CLASS_IDLE == 3); #endif #ifndef IOPRIO_WHO_PROCESS # define IOPRIO_WHO_PROCESS 1 +#else +assert_cc(IOPRIO_WHO_PROCESS == 1); #endif + #ifndef IOPRIO_WHO_PGRP # define IOPRIO_WHO_PGRP 2 +#else +assert_cc(IOPRIO_WHO_PGRP == 2); #endif + #ifndef IOPRIO_WHO_USER # define IOPRIO_WHO_USER 3 +#else +assert_cc(IOPRIO_WHO_USER == 3); #endif #ifndef IOPRIO_BITS # define IOPRIO_BITS 16 +#else +assert_cc(IOPRIO_BITS == 16); #endif + #ifndef IOPRIO_N_CLASSES # define IOPRIO_N_CLASSES 8 +#else +assert_cc(IOPRIO_N_CLASSES == 8); #endif + #ifndef IOPRIO_CLASS_SHIFT # define IOPRIO_CLASS_SHIFT 13 +#else +assert_cc(IOPRIO_CLASS_SHIFT == 13); #endif static inline int ioprio_prio_class(int value) { diff --git a/src/basic/missing_keyctl.h b/src/basic/missing_keyctl.h index 081003a..78795fa 100644 --- a/src/basic/missing_keyctl.h +++ b/src/basic/missing_keyctl.h @@ -4,40 +4,60 @@ #include <inttypes.h> #include <linux/keyctl.h> +#include "macro.h" + #ifndef KEYCTL_JOIN_SESSION_KEYRING -#define KEYCTL_JOIN_SESSION_KEYRING 1 +# define KEYCTL_JOIN_SESSION_KEYRING 1 +#else +assert_cc(KEYCTL_JOIN_SESSION_KEYRING == 1); #endif #ifndef KEYCTL_CHOWN -#define KEYCTL_CHOWN 4 +# define KEYCTL_CHOWN 4 +#else +assert_cc(KEYCTL_CHOWN == 4); #endif #ifndef KEYCTL_SETPERM -#define KEYCTL_SETPERM 5 +# define KEYCTL_SETPERM 5 +#else +assert_cc(KEYCTL_SETPERM == 5); #endif #ifndef KEYCTL_DESCRIBE -#define KEYCTL_DESCRIBE 6 +# define KEYCTL_DESCRIBE 6 +#else +assert_cc(KEYCTL_DESCRIBE == 6); #endif #ifndef KEYCTL_LINK -#define KEYCTL_LINK 8 +# define KEYCTL_LINK 8 +#else +assert_cc(KEYCTL_LINK == 8); #endif #ifndef KEYCTL_READ -#define KEYCTL_READ 11 +# define KEYCTL_READ 11 +#else +assert_cc(KEYCTL_READ == 11); #endif #ifndef KEYCTL_SET_TIMEOUT -#define KEYCTL_SET_TIMEOUT 15 +# define KEYCTL_SET_TIMEOUT 15 +#else +assert_cc(KEYCTL_SET_TIMEOUT == 15); #endif #ifndef KEY_SPEC_USER_KEYRING -#define KEY_SPEC_USER_KEYRING -4 +# define KEY_SPEC_USER_KEYRING -4 +#else +assert_cc(KEY_SPEC_USER_KEYRING == -4); #endif #ifndef KEY_SPEC_SESSION_KEYRING -#define KEY_SPEC_SESSION_KEYRING -3 +# define KEY_SPEC_SESSION_KEYRING -3 +#else +assert_cc(KEY_SPEC_SESSION_KEYRING == -3); #endif /* From linux/key.h */ @@ -45,35 +65,37 @@ typedef int32_t key_serial_t; -#define KEY_POS_VIEW 0x01000000 -#define KEY_POS_READ 0x02000000 -#define KEY_POS_WRITE 0x04000000 -#define KEY_POS_SEARCH 0x08000000 -#define KEY_POS_LINK 0x10000000 -#define KEY_POS_SETATTR 0x20000000 -#define KEY_POS_ALL 0x3f000000 - -#define KEY_USR_VIEW 0x00010000 -#define KEY_USR_READ 0x00020000 -#define KEY_USR_WRITE 0x00040000 -#define KEY_USR_SEARCH 0x00080000 -#define KEY_USR_LINK 0x00100000 -#define KEY_USR_SETATTR 0x00200000 -#define KEY_USR_ALL 0x003f0000 - -#define KEY_GRP_VIEW 0x00000100 -#define KEY_GRP_READ 0x00000200 -#define KEY_GRP_WRITE 0x00000400 -#define KEY_GRP_SEARCH 0x00000800 -#define KEY_GRP_LINK 0x00001000 -#define KEY_GRP_SETATTR 0x00002000 -#define KEY_GRP_ALL 0x00003f00 - -#define KEY_OTH_VIEW 0x00000001 -#define KEY_OTH_READ 0x00000002 -#define KEY_OTH_WRITE 0x00000004 -#define KEY_OTH_SEARCH 0x00000008 -#define KEY_OTH_LINK 0x00000010 -#define KEY_OTH_SETATTR 0x00000020 -#define KEY_OTH_ALL 0x0000003f +# define KEY_POS_VIEW 0x01000000 +# define KEY_POS_READ 0x02000000 +# define KEY_POS_WRITE 0x04000000 +# define KEY_POS_SEARCH 0x08000000 +# define KEY_POS_LINK 0x10000000 +# define KEY_POS_SETATTR 0x20000000 +# define KEY_POS_ALL 0x3f000000 + +# define KEY_USR_VIEW 0x00010000 +# define KEY_USR_READ 0x00020000 +# define KEY_USR_WRITE 0x00040000 +# define KEY_USR_SEARCH 0x00080000 +# define KEY_USR_LINK 0x00100000 +# define KEY_USR_SETATTR 0x00200000 +# define KEY_USR_ALL 0x003f0000 + +# define KEY_GRP_VIEW 0x00000100 +# define KEY_GRP_READ 0x00000200 +# define KEY_GRP_WRITE 0x00000400 +# define KEY_GRP_SEARCH 0x00000800 +# define KEY_GRP_LINK 0x00001000 +# define KEY_GRP_SETATTR 0x00002000 +# define KEY_GRP_ALL 0x00003f00 + +# define KEY_OTH_VIEW 0x00000001 +# define KEY_OTH_READ 0x00000002 +# define KEY_OTH_WRITE 0x00000004 +# define KEY_OTH_SEARCH 0x00000008 +# define KEY_OTH_LINK 0x00000010 +# define KEY_OTH_SETATTR 0x00000020 +# define KEY_OTH_ALL 0x0000003f +#else +assert_cc(KEY_OTH_ALL == 0x0000003f); #endif diff --git a/src/basic/missing_loop.h b/src/basic/missing_loop.h index 7141544..b88501d 100644 --- a/src/basic/missing_loop.h +++ b/src/basic/missing_loop.h @@ -3,6 +3,8 @@ #include <linux/loop.h> +#include "macro.h" + #ifndef LOOP_CONFIGURE struct loop_config { __u32 fd; @@ -11,14 +13,19 @@ struct loop_config { __u64 __reserved[8]; }; -#define LOOP_CONFIGURE 0x4C0A +# define LOOP_CONFIGURE 0x4C0A +#else +assert_cc(LOOP_CONFIGURE == 0x4C0A); #endif #ifndef LO_FLAGS_DIRECT_IO -#define LO_FLAGS_DIRECT_IO 16 -#define LOOP_SET_DIRECT_IO 0x4C08 +# define LO_FLAGS_DIRECT_IO 16 +# define LOOP_SET_DIRECT_IO 0x4C08 +#else +assert_cc(LO_FLAGS_DIRECT_IO == 16); +assert_cc(LOOP_SET_DIRECT_IO == 0x4C08); #endif #ifndef LOOP_SET_STATUS_SETTABLE_FLAGS -#define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN | LO_FLAGS_DIRECT_IO) +# define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN) #endif diff --git a/src/basic/missing_magic.h b/src/basic/missing_magic.h index 82d71c8..4e930ac 100644 --- a/src/basic/missing_magic.h +++ b/src/basic/missing_magic.h @@ -3,197 +3,107 @@ #include <linux/magic.h> -/* 62aa81d7c4c24b90fdb61da70ac0dbbc414f9939 (4.13) */ -#ifndef OCFS2_SUPER_MAGIC -#define OCFS2_SUPER_MAGIC 0x7461636f -#endif - -/* 67e9c74b8a873408c27ac9a8e4c1d1c8d72c93ff (4.5) */ -#ifndef CGROUP2_SUPER_MAGIC -#define CGROUP2_SUPER_MAGIC 0x63677270 -#endif - -/* 4282d60689d4f21b40692029080440cc58e8a17d (4.1) */ -#ifndef TRACEFS_MAGIC -#define TRACEFS_MAGIC 0x74726163 -#endif - -/* e149ed2b805fefdccf7ccdfc19eca22fdd4514ac (3.19) */ -#ifndef NSFS_MAGIC -#define NSFS_MAGIC 0x6e736673 -#endif - -/* b2197755b2633e164a439682fb05a9b5ea48f706 (4.4) */ -#ifndef BPF_FS_MAGIC -#define BPF_FS_MAGIC 0xcafe4a11 -#endif - /* Not exposed yet (4.20). Defined at ipc/mqueue.c */ #ifndef MQUEUE_MAGIC -#define MQUEUE_MAGIC 0x19800202 -#endif - -/* Not exposed yet (as of Linux 5.4). Defined in fs/xfs/libxfs/xfs_format.h */ -#ifndef XFS_SB_MAGIC -#define XFS_SB_MAGIC 0x58465342 -#endif - -/* dea2903719283c156b53741126228c4a1b40440f (5.17) */ -#ifndef CIFS_SUPER_MAGIC -#define CIFS_SUPER_MAGIC 0xFF534D42 -#endif - -/* dea2903719283c156b53741126228c4a1b40440f (5.17) */ -#ifndef SMB2_SUPER_MAGIC -#define SMB2_SUPER_MAGIC 0xFE534D42 -#endif - -/* 257f871993474e2bde6c497b54022c362cf398e1 (4.5) */ -#ifndef OVERLAYFS_SUPER_MAGIC -#define OVERLAYFS_SUPER_MAGIC 0x794c7630 -#endif - -/* 2a28900be20640fcd1e548b1e3bad79e8221fcf9 (4.7) */ -#ifndef UDF_SUPER_MAGIC -#define UDF_SUPER_MAGIC 0x15013346 +# define MQUEUE_MAGIC 0x19800202 +#else +assert_cc(MQUEUE_MAGIC == 0x19800202); #endif -/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8) */ +/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */ #ifndef BALLOON_KVM_MAGIC -#define BALLOON_KVM_MAGIC 0x13661366 +# define BALLOON_KVM_MAGIC 0x13661366 +#else +assert_cc(BALLOON_KVM_MAGIC == 0x13661366); #endif -/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8) */ +/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */ #ifndef ZSMALLOC_MAGIC -#define ZSMALLOC_MAGIC 0x58295829 -#endif - -/* 3bc52c45bac26bf7ed1dc8d287ad1aeaed1250b6 (4.9) */ -#ifndef DAXFS_MAGIC -#define DAXFS_MAGIC 0x64646178 -#endif - -/* 5ff193fbde20df5d80fec367cea3e7856c057320 (4.10) */ -#ifndef RDTGROUP_SUPER_MAGIC -#define RDTGROUP_SUPER_MAGIC 0x7655821 -#endif - -/* a481f4d917835cad86701fc0d1e620c74bb5cd5f (4.13) */ -#ifndef AAFS_MAGIC -#define AAFS_MAGIC 0x5a3c69f0 +# define ZSMALLOC_MAGIC 0x58295829 +#else +assert_cc(ZSMALLOC_MAGIC == 0x58295829); #endif -/* f044c8847bb61eff5e1e95b6f6bb950e7f4a73a4 (4.15) */ -#ifndef AFS_FS_MAGIC -#define AFS_FS_MAGIC 0x6b414653 -#endif - -/* dddde68b8f06dd83486124b8d245e7bfb15c185d (4.20) */ -#ifndef XFS_SUPER_MAGIC -#define XFS_SUPER_MAGIC 0x58465342 -#endif - -/* 3ad20fe393b31025bebfc2d76964561f65df48aa (5.0) */ -#ifndef BINDERFS_SUPER_MAGIC -#define BINDERFS_SUPER_MAGIC 0x6c6f6f70 -#endif - -/* ed63bb1d1f8469586006a9ca63c42344401aa2ab (5.3) */ -#ifndef DMA_BUF_MAGIC -#define DMA_BUF_MAGIC 0x444d4142 -#endif - -/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3) */ +/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */ #ifndef Z3FOLD_MAGIC -#define Z3FOLD_MAGIC 0x33 -#endif - -/* 47e4937a4a7ca4184fd282791dfee76c6799966a (5.4) */ -#ifndef EROFS_SUPER_MAGIC_V1 -#define EROFS_SUPER_MAGIC_V1 0xe0f5e1e2 +# define Z3FOLD_MAGIC 0x33 +#else +assert_cc(Z3FOLD_MAGIC == 0x33); #endif -/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5) */ +/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */ #ifndef PPC_CMM_MAGIC -#define PPC_CMM_MAGIC 0xc7571590 -#endif - -/* 8dcc1a9d90c10fa4143e5c17821082e5e60e46a1 (5.6) */ -#ifndef ZONEFS_MAGIC -#define ZONEFS_MAGIC 0x5a4f4653 -#endif - -/* 3234ac664a870e6ea69ae3a57d824cd7edbeacc5 (5.8) */ -#ifndef DEVMEM_MAGIC -#define DEVMEM_MAGIC 0x454d444d -#endif - -/* cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */ -#ifndef PID_FS_MAGIC -#define PID_FS_MAGIC 0x50494446 +# define PPC_CMM_MAGIC 0xc7571590 +#else +assert_cc(PPC_CMM_MAGIC == 0xc7571590); #endif /* Not in mainline but included in Ubuntu */ #ifndef SHIFTFS_MAGIC -#define SHIFTFS_MAGIC 0x6a656a62 -#endif - -/* 1507f51255c9ff07d75909a84e7c0d7f3c4b2f49 (5.14) */ -#ifndef SECRETMEM_MAGIC -#define SECRETMEM_MAGIC 0x5345434d -#endif - -/* Not exposed yet. Defined at fs/fuse/inode.c */ -#ifndef FUSE_SUPER_MAGIC -#define FUSE_SUPER_MAGIC 0x65735546 +# define SHIFTFS_MAGIC 0x6a656a62 +#else +assert_cc(SHIFTFS_MAGIC == 0x6a656a62); #endif /* Not exposed yet. Defined at fs/fuse/control.c */ #ifndef FUSE_CTL_SUPER_MAGIC -#define FUSE_CTL_SUPER_MAGIC 0x65735543 -#endif - -/* Not exposed yet. Defined at fs/ceph/super.h */ -#ifndef CEPH_SUPER_MAGIC -#define CEPH_SUPER_MAGIC 0x00c36400 +# define FUSE_CTL_SUPER_MAGIC 0x65735543 +#else +assert_cc(FUSE_CTL_SUPER_MAGIC == 0x65735543); #endif /* Not exposed yet. Defined at fs/orangefs/orangefs-kernel.h */ #ifndef ORANGEFS_DEVREQ_MAGIC -#define ORANGEFS_DEVREQ_MAGIC 0x20030529 +# define ORANGEFS_DEVREQ_MAGIC 0x20030529 +#else +assert_cc(ORANGEFS_DEVREQ_MAGIC == 0x20030529); #endif /* linux/gfs2_ondisk.h */ #ifndef GFS2_MAGIC -#define GFS2_MAGIC 0x01161970 +# define GFS2_MAGIC 0x01161970 +#else +assert_cc(GFS2_MAGIC == 0x01161970); #endif /* Not exposed yet. Defined at fs/configfs/mount.c */ #ifndef CONFIGFS_MAGIC -#define CONFIGFS_MAGIC 0x62656570 +# define CONFIGFS_MAGIC 0x62656570 +#else +assert_cc(CONFIGFS_MAGIC == 0x62656570); #endif /* Not exposed yet. Defined at fs/vboxsf/super.c */ #ifndef VBOXSF_SUPER_MAGIC -#define VBOXSF_SUPER_MAGIC 0x786f4256 -#endif - -/* Not exposed yet. Defined at fs/exfat/exfat_fs.h */ -#ifndef EXFAT_SUPER_MAGIC -#define EXFAT_SUPER_MAGIC 0x2011BAB0UL +# define VBOXSF_SUPER_MAGIC 0x786f4256 +#else +assert_cc(VBOXSF_SUPER_MAGIC == 0x786f4256); #endif /* Not exposed yet, internally actually called RPCAUTH_GSSMAGIC. Defined in net/sunrpc/rpc_pipe.c */ #ifndef RPC_PIPEFS_SUPER_MAGIC -#define RPC_PIPEFS_SUPER_MAGIC 0x67596969 +# define RPC_PIPEFS_SUPER_MAGIC 0x67596969 +#else +assert_cc(RPC_PIPEFS_SUPER_MAGIC == 0x67596969); #endif /* Not exposed yet, defined at fs/ntfs/ntfs.h */ #ifndef NTFS_SB_MAGIC -#define NTFS_SB_MAGIC 0x5346544e +# define NTFS_SB_MAGIC 0x5346544e +#else +assert_cc(NTFS_SB_MAGIC == 0x5346544e); #endif /* Not exposed yet, encoded literally in fs/ntfs3/super.c. */ #ifndef NTFS3_SUPER_MAGIC -#define NTFS3_SUPER_MAGIC 0x7366746e +# define NTFS3_SUPER_MAGIC 0x7366746e +#else +assert_cc(NTFS3_SUPER_MAGIC == 0x7366746e); +#endif + +/* Added in Linux commit e2f48c48090dea172c0c571101041de64634dae5. Remove when next sync'd */ +#ifndef BCACHEFS_SUPER_MAGIC +# define BCACHEFS_SUPER_MAGIC 0xca451a4e +#else +assert_cc(BCACHEFS_SUPER_MAGIC == 0xca451a4e) #endif diff --git a/src/basic/missing_mman.h b/src/basic/missing_mman.h index f48c436..d6a8b4b 100644 --- a/src/basic/missing_mman.h +++ b/src/basic/missing_mman.h @@ -3,18 +3,28 @@ #include <sys/mman.h> +#include "macro.h" + #ifndef MFD_ALLOW_SEALING -#define MFD_ALLOW_SEALING 0x0002U +# define MFD_ALLOW_SEALING 0x0002U +#else +assert_cc(MFD_ALLOW_SEALING == 0x0002U); #endif #ifndef MFD_CLOEXEC -#define MFD_CLOEXEC 0x0001U +# define MFD_CLOEXEC 0x0001U +#else +assert_cc(MFD_CLOEXEC == 0x0001U); #endif #ifndef MFD_NOEXEC_SEAL -#define MFD_NOEXEC_SEAL 0x0008U +# define MFD_NOEXEC_SEAL 0x0008U +#else +assert_cc(MFD_NOEXEC_SEAL == 0x0008U); #endif #ifndef MFD_EXEC -#define MFD_EXEC 0x0010U +# define MFD_EXEC 0x0010U +#else +assert_cc(MFD_EXEC == 0x0010U); #endif diff --git a/src/basic/missing_mount.h b/src/basic/missing_mount.h index 69b0bcf..d6e16e5 100644 --- a/src/basic/missing_mount.h +++ b/src/basic/missing_mount.h @@ -3,7 +3,11 @@ #include <sys/mount.h> +#include "macro.h" + /* dab741e0e02bd3c4f5e2e97be74b39df2523fc6e (5.10) */ #ifndef MS_NOSYMFOLLOW -#define MS_NOSYMFOLLOW 256 +# define MS_NOSYMFOLLOW 256 +#else +assert_cc(MS_NOSYMFOLLOW == 256); #endif diff --git a/src/basic/missing_prctl.h b/src/basic/missing_prctl.h index 7d9e395..2c9f9f6 100644 --- a/src/basic/missing_prctl.h +++ b/src/basic/missing_prctl.h @@ -3,6 +3,8 @@ #include <linux/prctl.h> +#include "macro.h" + /* 58319057b7847667f0c9585b9de0e8932b0fdb08 (4.3) */ #ifndef PR_CAP_AMBIENT #define PR_CAP_AMBIENT 47 @@ -15,12 +17,19 @@ /* b507808ebce23561d4ff8c2aa1fb949fe402bc61 (6.3) */ #ifndef PR_SET_MDWE -#define PR_SET_MDWE 65 +# define PR_SET_MDWE 65 +#else +assert_cc(PR_SET_MDWE == 65); #endif + #ifndef PR_MDWE_REFUSE_EXEC_GAIN -#define PR_MDWE_REFUSE_EXEC_GAIN 1 +# define PR_MDWE_REFUSE_EXEC_GAIN 1 +#else +assert_cc(PR_MDWE_REFUSE_EXEC_GAIN == 1); #endif #ifndef PR_SET_MEMORY_MERGE -#define PR_SET_MEMORY_MERGE 67 +# define PR_SET_MEMORY_MERGE 67 +#else +assert_cc(PR_SET_MEMORY_MERGE == 67); #endif diff --git a/src/basic/missing_random.h b/src/basic/missing_random.h index 443b913..0f8a5be 100644 --- a/src/basic/missing_random.h +++ b/src/basic/missing_random.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "macro.h" + #if USE_SYS_RANDOM_H # include <sys/random.h> #else @@ -8,13 +10,19 @@ #endif #ifndef GRND_NONBLOCK -#define GRND_NONBLOCK 0x0001 +# define GRND_NONBLOCK 0x0001 +#else +assert_cc(GRND_NONBLOCK == 0x0001); #endif #ifndef GRND_RANDOM -#define GRND_RANDOM 0x0002 +# define GRND_RANDOM 0x0002 +#else +assert_cc(GRND_RANDOM == 0x0002); #endif #ifndef GRND_INSECURE -#define GRND_INSECURE 0x0004 +# define GRND_INSECURE 0x0004 +#else +assert_cc(GRND_INSECURE == 0x0004); #endif diff --git a/src/basic/missing_resource.h b/src/basic/missing_resource.h index 6e76765..1807673 100644 --- a/src/basic/missing_resource.h +++ b/src/basic/missing_resource.h @@ -3,8 +3,12 @@ #include <sys/resource.h> +#include "macro.h" + #ifndef RLIMIT_RTTIME -#define RLIMIT_RTTIME 15 +# define RLIMIT_RTTIME 15 +#else +assert_cc(RLIMIT_RTTIME == 15); #endif /* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */ diff --git a/src/basic/missing_sched.h b/src/basic/missing_sched.h index bcd5b77..b8109d3 100644 --- a/src/basic/missing_sched.h +++ b/src/basic/missing_sched.h @@ -3,24 +3,35 @@ #include <sched.h> +#include "macro.h" + #ifndef CLONE_NEWCGROUP -#define CLONE_NEWCGROUP 0x02000000 +# define CLONE_NEWCGROUP 0x02000000 +#else +assert_cc(CLONE_NEWCGROUP == 0x02000000); #endif /* 769071ac9f20b6a447410c7eaa55d1a5233ef40c (5.8) */ #ifndef CLONE_NEWTIME -#define CLONE_NEWTIME 0x00000080 +# define CLONE_NEWTIME 0x00000080 +#else +assert_cc(CLONE_NEWTIME == 0x00000080); #endif /* Not exposed yet. Defined at include/linux/sched.h */ #ifndef PF_KTHREAD -#define PF_KTHREAD 0x00200000 +# define PF_KTHREAD 0x00200000 +#else +assert_cc(PF_KTHREAD == 0x00200000); #endif -/* The maximum thread/process name length including trailing NUL byte. This mimics the kernel definition of the same - * name, which we need in userspace at various places but is not defined in userspace currently, neither under this - * name nor any other. */ -/* Not exposed yet. Defined at include/linux/sched.h */ +/* The maximum thread/process name length including trailing NUL byte. This mimics the kernel definition of + * the same name, which we need in userspace at various places but is not defined in userspace currently, + * neither under this name nor any other. + * + * Not exposed yet. Defined at include/linux/sched.h */ #ifndef TASK_COMM_LEN -#define TASK_COMM_LEN 16 +# define TASK_COMM_LEN 16 +#else +assert_cc(TASK_COMM_LEN == 16); #endif diff --git a/src/basic/missing_socket.h b/src/basic/missing_socket.h index 30ac297..47cc762 100644 --- a/src/basic/missing_socket.h +++ b/src/basic/missing_socket.h @@ -6,7 +6,6 @@ #if HAVE_LINUX_VM_SOCKETS_H #include <linux/vm_sockets.h> #else -#define VMADDR_CID_ANY -1U struct sockaddr_vm { unsigned short svm_family; unsigned short svm_reserved1; @@ -20,6 +19,26 @@ struct sockaddr_vm { }; #endif /* !HAVE_LINUX_VM_SOCKETS_H */ +#ifndef VMADDR_CID_ANY +#define VMADDR_CID_ANY -1U +#endif + +#ifndef VMADDR_CID_HYPERVISOR +#define VMADDR_CID_HYPERVISOR 0U +#endif + +#ifndef VMADDR_CID_LOCAL +#define VMADDR_CID_LOCAL 1U +#endif + +#ifndef VMADDR_CID_HOST +#define VMADDR_CID_HOST 2U +#endif + +#ifndef VMADDR_PORT_ANY +#define VMADDR_PORT_ANY -1U +#endif + #ifndef AF_VSOCK #define AF_VSOCK 40 #endif @@ -32,6 +51,10 @@ struct sockaddr_vm { #define SO_PEERGROUPS 59 #endif +#ifndef SO_PEERPIDFD +#define SO_PEERPIDFD 77 +#endif + #ifndef SO_BINDTOIFINDEX #define SO_BINDTOIFINDEX 62 #endif diff --git a/src/basic/missing_timerfd.h b/src/basic/missing_timerfd.h index dba3043..a01a4ec 100644 --- a/src/basic/missing_timerfd.h +++ b/src/basic/missing_timerfd.h @@ -3,6 +3,10 @@ #include <sys/timerfd.h> +#include "macro.h" + #ifndef TFD_TIMER_CANCEL_ON_SET -#define TFD_TIMER_CANCEL_ON_SET (1 << 1) +# define TFD_TIMER_CANCEL_ON_SET (1 << 1) +#else +assert_cc(TFD_TIMER_CANCEL_ON_SET == (1 << 1)); #endif diff --git a/src/basic/missing_type.h b/src/basic/missing_type.h index f623309..1d17705 100644 --- a/src/basic/missing_type.h +++ b/src/basic/missing_type.h @@ -4,9 +4,9 @@ #include <uchar.h> #if !HAVE_CHAR32_T -#define char32_t uint32_t +# define char32_t uint32_t #endif #if !HAVE_CHAR16_T -#define char16_t uint16_t +# define char16_t uint16_t #endif diff --git a/src/basic/missing_wait.h b/src/basic/missing_wait.h new file mode 100644 index 0000000..3965b5b --- /dev/null +++ b/src/basic/missing_wait.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <sys/wait.h> + +#include "macro.h" + +#ifndef P_PIDFD +# define P_PIDFD 3 +#else +assert_cc(P_PIDFD == 3); +#endif diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index c770e5e..f87de0a 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -70,17 +70,11 @@ int mkdirat_safe_internal( path, st.st_mode & 0777, mode); if ((uid != UID_INVALID && st.st_uid != uid) || - (gid != GID_INVALID && st.st_gid != gid)) { - char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-"; - - if (uid != UID_INVALID) - xsprintf(u, UID_FMT, uid); - if (gid != UID_INVALID) - xsprintf(g, GID_FMT, gid); + (gid != GID_INVALID && st.st_gid != gid)) return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST), "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.", - path, st.st_uid, st.st_gid, u, g); - } + path, st.st_uid, st.st_gid, uid != UID_INVALID ? FORMAT_UID(uid) : "-", + gid != UID_INVALID ? FORMAT_GID(gid) : "-"); return 0; } @@ -118,7 +112,7 @@ int mkdirat_parents_internal(int dir_fd, const char *path, mode_t mode, uid_t ui /* drop the last component */ path = strndupa_safe(path, e - path); - r = is_dir_full(dir_fd, path, true); + r = is_dir_at(dir_fd, path, /* follow = */ true); if (r > 0) return 0; if (r == 0) @@ -210,11 +204,13 @@ int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, g return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper); } -int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes) { +int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes) { _cleanup_free_ char *pp = NULL, *bn = NULL; _cleanup_close_ int dfd = -EBADF; int r; + assert(p); + r = path_extract_directory(p, &pp); if (r == -EDESTADDRREQ) { /* only fname is passed, no prefix to operate on */ @@ -228,11 +224,11 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m return r; else { /* Extracting the parent dir worked, hence we aren't top-level? Recurse up first. */ - r = mkdir_p_root(root, pp, uid, gid, m, subvolumes); + r = mkdir_p_root_full(root, pp, uid, gid, m, ts, subvolumes); if (r < 0) return r; - dfd = chase_and_open(pp, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_DIRECTORY, NULL); + dfd = chase_and_open(pp, root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_DIRECTORY, NULL); if (dfd < 0) return dfd; } @@ -247,23 +243,31 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m r = btrfs_subvol_make_fallback(dfd, bn, m); else r = RET_NERRNO(mkdirat(dfd, bn, m)); - if (r < 0) { - if (r == -EEXIST) - return 0; - + if (r == -EEXIST) + return 0; + if (r < 0) return r; - } - if (uid_is_valid(uid) || gid_is_valid(gid)) { - _cleanup_close_ int nfd = -EBADF; + if (ts == USEC_INFINITY && !uid_is_valid(uid) && !gid_is_valid(gid)) + return 1; + + _cleanup_close_ int nfd = openat(dfd, bn, O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); + if (nfd < 0) + return -errno; + + if (ts != USEC_INFINITY) { + struct timespec tspec; + timespec_store(&tspec, ts); - nfd = openat(dfd, bn, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); - if (nfd < 0) + if (futimens(dfd, (const struct timespec[2]) { TIMESPEC_OMIT, tspec }) < 0) return -errno; - if (fchown(nfd, uid, gid) < 0) + if (futimens(nfd, (const struct timespec[2]) { tspec, tspec }) < 0) return -errno; } + if ((uid_is_valid(uid) || gid_is_valid(gid)) && fchown(nfd, uid, gid) < 0) + return -errno; + return 1; } diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h index e538748..471f45b 100644 --- a/src/basic/mkdir.h +++ b/src/basic/mkdir.h @@ -4,6 +4,8 @@ #include <fcntl.h> #include <sys/types.h> +#include "time-util.h" + typedef enum MkdirFlags { MKDIR_FOLLOW_SYMLINK = 1 << 0, MKDIR_IGNORE_EXISTING = 1 << 1, /* Quietly accept a preexisting directory (or file) */ @@ -23,7 +25,10 @@ static inline int mkdir_parents(const char *path, mode_t mode) { int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); int mkdir_p(const char *path, mode_t mode); int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); -int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes); +int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes); +static inline int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m) { + return mkdir_p_root_full(root, p, uid, gid, m, USEC_INFINITY, NULL); +} /* The following are used to implement the mkdir_xyz_label() calls, don't use otherwise. */ typedef int (*mkdirat_func_t)(int dir_fd, const char *pathname, mode_t mode); diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index bf67f7e..66fa35b 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -329,34 +329,33 @@ fallback_fstat: } /* flags can be AT_SYMLINK_FOLLOW or 0 */ -int path_is_mount_point(const char *t, const char *root, int flags) { +int path_is_mount_point_full(const char *path, const char *root, int flags) { _cleanup_free_ char *canonical = NULL; _cleanup_close_ int fd = -EBADF; int r; - assert(t); + assert(path); assert((flags & ~AT_SYMLINK_FOLLOW) == 0); - if (path_equal(t, "/")) + if (path_equal(path, "/")) return 1; - /* we need to resolve symlinks manually, we can't just rely on - * fd_is_mount_point() to do that for us; if we have a structure like - * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we + /* we need to resolve symlinks manually, we can't just rely on fd_is_mount_point() to do that for us; + * if we have a structure like /bin -> /usr/bin/ and /usr is a mount point, then the parent that we * look at needs to be /usr, not /. */ - if (flags & AT_SYMLINK_FOLLOW) { - r = chase(t, root, CHASE_TRAIL_SLASH, &canonical, NULL); + if (FLAGS_SET(flags, AT_SYMLINK_FOLLOW)) { + r = chase(path, root, CHASE_TRAIL_SLASH, &canonical, NULL); if (r < 0) return r; - t = canonical; + path = canonical; } - fd = open_parent(t, O_PATH|O_CLOEXEC, 0); + fd = open_parent(path, O_PATH|O_CLOEXEC, 0); if (fd < 0) return fd; - return fd_is_mount_point(fd, last_path_component(t), flags); + return fd_is_mount_point(fd, last_path_component(path), flags); } int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) { @@ -446,14 +445,15 @@ bool fstype_needs_quota(const char *fstype) { } bool fstype_is_api_vfs(const char *fstype) { - const FilesystemSet *fs; + assert(fstype); - FOREACH_POINTER(fs, - filesystem_sets + FILESYSTEM_SET_BASIC_API, - filesystem_sets + FILESYSTEM_SET_AUXILIARY_API, - filesystem_sets + FILESYSTEM_SET_PRIVILEGED_API, - filesystem_sets + FILESYSTEM_SET_TEMPORARY) - if (nulstr_contains(fs->value, fstype)) + const FilesystemSet *fs; + FOREACH_ARGUMENT(fs, + filesystem_sets + FILESYSTEM_SET_BASIC_API, + filesystem_sets + FILESYSTEM_SET_AUXILIARY_API, + filesystem_sets + FILESYSTEM_SET_PRIVILEGED_API, + filesystem_sets + FILESYSTEM_SET_TEMPORARY) + if (nulstr_contains(fs->value, fstype)) return true; /* Filesystems not present in the internal database */ @@ -495,16 +495,34 @@ bool fstype_can_discard(const char *fstype) { return mount_option_supported(fstype, "discard", NULL) > 0; } -bool fstype_can_norecovery(const char *fstype) { +const char* fstype_norecovery_option(const char *fstype) { + int r; + assert(fstype); /* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might * not be allowed in our MAC context. */ - if (STR_IN_SET(fstype, "ext3", "ext4", "xfs", "btrfs")) - return true; + if (STR_IN_SET(fstype, "ext3", "ext4", "xfs")) + return "norecovery"; + + /* btrfs dropped support for the "norecovery" option in 6.8 + * (https://github.com/torvalds/linux/commit/a1912f712188291f9d7d434fba155461f1ebef66) and replaced + * it with rescue=nologreplay, so we check for the new name first and fall back to checking for the + * old name if the new name doesn't work. */ + if (streq(fstype, "btrfs")) { + r = mount_option_supported(fstype, "rescue=nologreplay", NULL); + if (r == -EAGAIN) { + log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming old kernel with 'norecovery': %m"); + return "norecovery"; + } + if (r < 0) + log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming it is not supported: %m"); + if (r > 0) + return "rescue=nologreplay"; + } /* On new kernels we can just ask the kernel */ - return mount_option_supported(fstype, "norecovery", NULL) > 0; + return mount_option_supported(fstype, "norecovery", NULL) > 0 ? "norecovery" : NULL; } bool fstype_can_umask(const char *fstype) { @@ -784,3 +802,10 @@ int mount_option_supported(const char *fstype, const char *key, const char *valu return true; /* works! */ } + +bool path_below_api_vfs(const char *p) { + assert(p); + + /* API VFS are either directly mounted on any of these three paths, or below it. */ + return PATH_STARTSWITH_SET(p, "/dev", "/sys", "/proc"); +} diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h index 499403a..d7c6251 100644 --- a/src/basic/mountpoint-util.h +++ b/src/basic/mountpoint-util.h @@ -3,6 +3,7 @@ #include <fcntl.h> #include <stdbool.h> +#include <stddef.h> #include <sys/types.h> /* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't @@ -44,7 +45,10 @@ static inline int path_get_mnt_id(const char *path, int *ret) { } int fd_is_mount_point(int fd, const char *filename, int flags); -int path_is_mount_point(const char *path, const char *root, int flags); +int path_is_mount_point_full(const char *path, const char *root, int flags); +static inline int path_is_mount_point(const char *path) { + return path_is_mount_point_full(path, NULL, 0); +} bool fstype_is_network(const char *fstype); bool fstype_needs_quota(const char *fstype); @@ -53,9 +57,10 @@ bool fstype_is_blockdev_backed(const char *fstype); bool fstype_is_ro(const char *fsype); bool fstype_can_discard(const char *fstype); bool fstype_can_uid_gid(const char *fstype); -bool fstype_can_norecovery(const char *fstype); bool fstype_can_umask(const char *fstype); +const char* fstype_norecovery_option(const char *fstype); + int dev_is_devtmpfs(void); int mount_fd(const char *source, int target_fd, const char *filesystemtype, unsigned long mountflags, const void *data); @@ -69,3 +74,5 @@ bool mount_new_api_supported(void); unsigned long ms_nosymfollow_supported(void); int mount_option_supported(const char *fstype, const char *key, const char *value); + +bool path_below_api_vfs(const char *p); diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index 2101f61..5b4e43f 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -11,6 +11,7 @@ #include "missing_magic.h" #include "missing_sched.h" #include "namespace-util.h" +#include "parse-util.h" #include "process-util.h" #include "stat-util.h" #include "stdio-util.h" @@ -33,71 +34,86 @@ const struct namespace_info namespace_info[] = { #define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path) -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, netnsfd = -EBADF, usernsfd = -EBADF; - int rfd = -EBADF; +static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) { + for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++) + if (((namespace_info[t].clone_flag ^ clone_flag) & (CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUSER|CLONE_NEWUTS|CLONE_NEWTIME)) == 0) + return t; + + return _NAMESPACE_TYPE_INVALID; +} + +int namespace_open( + pid_t pid, + int *ret_pidns_fd, + int *ret_mntns_fd, + int *ret_netns_fd, + int *ret_userns_fd, + int *ret_root_fd) { + + _cleanup_close_ int pidns_fd = -EBADF, mntns_fd = -EBADF, netns_fd = -EBADF, + userns_fd = -EBADF, root_fd = -EBADF; assert(pid >= 0); - if (mntns_fd) { - const char *mntns; + if (ret_pidns_fd) { + const char *pidns; - mntns = pid_namespace_path(pid, NAMESPACE_MOUNT); - mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntnsfd < 0) + pidns = pid_namespace_path(pid, NAMESPACE_PID); + pidns_fd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (pidns_fd < 0) return -errno; } - if (pidns_fd) { - const char *pidns; + if (ret_mntns_fd) { + const char *mntns; - pidns = pid_namespace_path(pid, NAMESPACE_PID); - pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (pidnsfd < 0) + mntns = pid_namespace_path(pid, NAMESPACE_MOUNT); + mntns_fd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (mntns_fd < 0) return -errno; } - if (netns_fd) { + if (ret_netns_fd) { const char *netns; netns = pid_namespace_path(pid, NAMESPACE_NET); - netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netnsfd < 0) + netns_fd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (netns_fd < 0) return -errno; } - if (userns_fd) { + if (ret_userns_fd) { const char *userns; userns = pid_namespace_path(pid, NAMESPACE_USER); - usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (usernsfd < 0 && errno != ENOENT) + userns_fd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (userns_fd < 0 && errno != ENOENT) return -errno; } - if (root_fd) { + if (ret_root_fd) { const char *root; root = procfs_file_alloca(pid, "root"); - rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rfd < 0) + root_fd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (root_fd < 0) return -errno; } - if (pidns_fd) - *pidns_fd = TAKE_FD(pidnsfd); + if (ret_pidns_fd) + *ret_pidns_fd = TAKE_FD(pidns_fd); - if (mntns_fd) - *mntns_fd = TAKE_FD(mntnsfd); + if (ret_mntns_fd) + *ret_mntns_fd = TAKE_FD(mntns_fd); - if (netns_fd) - *netns_fd = TAKE_FD(netnsfd); + if (ret_netns_fd) + *ret_netns_fd = TAKE_FD(netns_fd); - if (userns_fd) - *userns_fd = TAKE_FD(usernsfd); + if (ret_userns_fd) + *ret_userns_fd = TAKE_FD(userns_fd); - if (root_fd) - *root_fd = TAKE_FD(rfd); + if (ret_root_fd) + *ret_root_fd = TAKE_FD(root_fd); return 0; } @@ -206,6 +222,88 @@ int detach_mount_namespace(void) { return 0; } +int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid) { + int r; + + /* Tried detach_mount_namespace() first. If that doesn't work due to permissions, opens up an + * unprivileged user namespace with a mapping of the originating UID/GID to the specified target + * UID/GID. Then, tries detach_mount_namespace() again. + * + * Or in other words: tries much harder to get a mount namespace, making use of unprivileged user + * namespaces if need be. + * + * Note that after this function completed: + * + * → if we had privs, afterwards uids/gids on files and processes are as before + * + * → if we had no privs, our own id and all our files will show up owned by target_uid/target_gid, + * and everything else owned by nobody. + * + * Yes, that's quite a difference. */ + + if (!uid_is_valid(target_uid)) + return -EINVAL; + if (!gid_is_valid(target_gid)) + return -EINVAL; + + r = detach_mount_namespace(); + if (r != -EPERM) + return r; + + if (unshare(CLONE_NEWUSER) < 0) + return log_debug_errno(errno, "Failed to acquire user namespace: %m"); + + r = write_string_filef("/proc/self/uid_map", 0, + UID_FMT " " UID_FMT " 1\n", target_uid, getuid()); + if (r < 0) + return log_debug_errno(r, "Failed to write uid map: %m"); + + r = write_string_file("/proc/self/setgroups", "deny", 0); + if (r < 0) + return log_debug_errno(r, "Failed to write setgroups file: %m"); + + r = write_string_filef("/proc/self/gid_map", 0, + GID_FMT " " GID_FMT " 1\n", target_gid, getgid()); + if (r < 0) + return log_debug_errno(r, "Failed to write gid map: %m"); + + return detach_mount_namespace(); +} + +int detach_mount_namespace_userns(int userns_fd) { + int r; + + assert(userns_fd >= 0); + + if (setns(userns_fd, CLONE_NEWUSER) < 0) + return log_debug_errno(errno, "Failed to join user namespace: %m"); + + r = reset_uid_gid(); + if (r < 0) + return log_debug_errno(r, "Failed to become root in user namespace: %m"); + + return detach_mount_namespace(); +} + +int userns_acquire_empty(void) { + _cleanup_(sigkill_waitp) pid_t pid = 0; + _cleanup_close_ int userns_fd = -EBADF; + int r; + + r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); + if (r < 0) + return r; + if (r == 0) + /* Child. We do nothing here, just freeze until somebody kills us. */ + freeze(); + + r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL); + if (r < 0) + return log_error_errno(r, "Failed to open userns fd: %m"); + + return TAKE_FD(userns_fd); +} + int userns_acquire(const char *uid_map, const char *gid_map) { char path[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; _cleanup_(sigkill_waitp) pid_t pid = 0; @@ -221,7 +319,7 @@ int userns_acquire(const char *uid_map, const char *gid_map) { r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); if (r < 0) - return r; + return log_debug_errno(r, "Failed to fork process (sd-mkuserns): %m"); if (r == 0) /* Child. We do nothing here, just freeze until somebody kills us. */ freeze(); @@ -229,19 +327,50 @@ int userns_acquire(const char *uid_map, const char *gid_map) { xsprintf(path, "/proc/" PID_FMT "/uid_map", pid); r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return log_error_errno(r, "Failed to write UID map: %m"); + return log_debug_errno(r, "Failed to write UID map: %m"); xsprintf(path, "/proc/" PID_FMT "/gid_map", pid); r = write_string_file(path, gid_map, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return log_error_errno(r, "Failed to write GID map: %m"); - - r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL); + return log_debug_errno(r, "Failed to write GID map: %m"); + + r = namespace_open(pid, + /* ret_pidns_fd = */ NULL, + /* ret_mntns_fd = */ NULL, + /* ret_netns_fd = */ NULL, + &userns_fd, + /* ret_root_fd = */ NULL); if (r < 0) - return log_error_errno(r, "Failed to open userns fd: %m"); + return log_debug_errno(r, "Failed to open userns fd: %m"); return TAKE_FD(userns_fd); +} +int netns_acquire(void) { + _cleanup_(sigkill_waitp) pid_t pid = 0; + _cleanup_close_ int netns_fd = -EBADF; + int r; + + /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills + * the process again. This way we have a netns fd that is not bound to any process. */ + + r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid); + if (r < 0) + return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m"); + if (r == 0) + /* Child. We do nothing here, just freeze until somebody kills us. */ + freeze(); + + r = namespace_open(pid, + /* ret_pidns_fd = */ NULL, + /* ret_mntns_fd = */ NULL, + &netns_fd, + /* ret_userns_fd = */ NULL, + /* ret_root_fd = */ NULL); + if (r < 0) + return log_debug_errno(r, "Failed to open netns fd: %m"); + + return TAKE_FD(netns_fd); } int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) { @@ -267,3 +396,88 @@ int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) { return stat_inode_same(&ns_st1, &ns_st2); } + +int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) { + _cleanup_free_ char *buffer = NULL; + const char *range, *shift; + int r; + uid_t uid_shift, uid_range = 65536; + + assert(s); + + range = strchr(s, ':'); + if (range) { + buffer = strndup(s, range - s); + if (!buffer) + return log_oom(); + shift = buffer; + + range++; + r = safe_atou32(range, &uid_range); + if (r < 0) + return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range); + } else + shift = s; + + r = parse_uid(shift, &uid_shift); + if (r < 0) + return log_error_errno(r, "Failed to parse UID \"%s\": %m", s); + + if (!userns_shift_range_valid(uid_shift, uid_range)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID); + + if (ret_uid_shift) + *ret_uid_shift = uid_shift; + + if (ret_uid_range) + *ret_uid_range = uid_range; + + return 0; +} + +int namespace_open_by_type(NamespaceType type) { + const char *p; + int fd; + + assert(type >= 0); + assert(type < _NAMESPACE_TYPE_MAX); + + p = pid_namespace_path(0, type); + + fd = RET_NERRNO(open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC)); + if (fd == -ENOENT && proc_mounted() == 0) + return -ENOSYS; + + return fd; +} + +int is_our_namespace(int fd, NamespaceType request_type) { + int clone_flag; + + assert(fd >= 0); + + clone_flag = ioctl(fd, NS_GET_NSTYPE); + if (clone_flag < 0) + return -errno; + + NamespaceType found_type = clone_flag_to_namespace_type(clone_flag); + if (found_type < 0) + return -EBADF; /* Uh? Unknown namespace type? */ + + if (request_type >= 0 && request_type != found_type) /* It's a namespace, but not of the right type? */ + return -EUCLEAN; + + struct stat st_fd, st_ours; + if (fstat(fd, &st_fd) < 0) + return -errno; + + const char *p = pid_namespace_path(0, found_type); + if (stat(p, &st_ours) < 0) { + if (errno == ENOENT) + return proc_mounted() == 0 ? -ENOSYS : -ENOENT; + + return -errno; + } + + return stat_inode_same(&st_ours, &st_fd); +} diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h index be5b228..545952a 100644 --- a/src/basic/namespace-util.h +++ b/src/basic/namespace-util.h @@ -22,12 +22,20 @@ extern const struct namespace_info { unsigned int clone_flag; } namespace_info[_NAMESPACE_TYPE_MAX + 1]; -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd); +int namespace_open( + pid_t pid, + int *ret_pidns_fd, + int *ret_mntns_fd, + int *ret_netns_fd, + int *ret_userns_fd, + int *ret_root_fd); int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); int fd_is_ns(int fd, unsigned long nsflag); int detach_mount_namespace(void); +int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid); +int detach_mount_namespace_userns(int userns_fd); static inline bool userns_shift_range_valid(uid_t shift, uid_t range) { /* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end @@ -44,5 +52,15 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) { return true; } +int userns_acquire_empty(void); int userns_acquire(const char *uid_map, const char *gid_map); + +int netns_acquire(void); + int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type); + +int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range); + +int namespace_open_by_type(NamespaceType type); + +int is_our_namespace(int fd, NamespaceType type); diff --git a/src/basic/nulstr-util.c b/src/basic/nulstr-util.c index 06fa219..7097a2c 100644 --- a/src/basic/nulstr-util.c +++ b/src/basic/nulstr-util.c @@ -4,7 +4,21 @@ #include "string-util.h" #include "strv.h" +const char* nulstr_get(const char *nulstr, const char *needle) { + if (!nulstr) + return NULL; + + NULSTR_FOREACH(i, nulstr) + if (streq(i, needle)) + return i; + + return NULL; +} + char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls) { + _cleanup_strv_free_ char **v = NULL; + size_t c = 0, i = 0; + /* l is the length of the input data, which will be split at NULs into elements of the resulting * strv. Hence, the number of items in the resulting strv will be equal to one plus the number of NUL * bytes in the l bytes starting at s, unless s[l-1] is NUL, in which case the final empty string is @@ -13,9 +27,6 @@ char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls) * Note that contrary to a normal nulstr which cannot contain empty strings, because the input data * is terminated by any two consequent NUL bytes, this parser accepts empty strings in s. */ - _cleanup_strv_free_ char **v = NULL; - size_t c = 0, i = 0; - assert(s || l <= 0); if (drop_trailing_nuls) @@ -36,7 +47,7 @@ char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls) if (!v) return NULL; - for (const char *p = s; p < s + l; ) { + for (const char *p = s; p < s + l;) { const char *e; e = memchr(p, 0, s + l - p); @@ -44,7 +55,6 @@ char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls) v[i] = memdup_suffix0(p, e ? e - p : s + l - p); if (!v[i]) return NULL; - i++; if (!e) @@ -74,6 +84,9 @@ char** strv_split_nulstr(const char *s) { } int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { + _cleanup_free_ char *m = NULL; + size_t n = 0; + /* Builds a nulstr and returns it together with the size. An extra NUL byte will be appended (⚠️ but * not included in the size! ⚠️). This is done so that the nulstr can be used both in * strv_parse_nulstr() and in NULSTR_FOREACH()/strv_split_nulstr() contexts, i.e. with and without a @@ -84,21 +97,18 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { * NUL bytes (which it will, if not empty). To ensure that this assumption *always* holds, we'll * return a buffer with two NUL bytes in that case, but return a size of zero. */ - _cleanup_free_ char *m = NULL; - size_t n = 0; - assert(ret); STRV_FOREACH(i, l) { size_t z; - z = strlen(*i); + z = strlen(*i) + 1; - if (!GREEDY_REALLOC(m, n + z + 2)) + if (!GREEDY_REALLOC(m, n + z + 1)) /* One extra NUL at the end as marker */ return -ENOMEM; - memcpy(m + n, *i, z + 1); - n += z + 1; + memcpy(m + n, *i, z); + n += z; } if (!m) { @@ -109,7 +119,7 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { n = 0; } else - /* Make sure there is a second extra NUL at the end of resulting nulstr (not counted in return size) */ + /* Extra NUL is not counted in size returned */ m[n] = '\0'; *ret = TAKE_PTR(m); @@ -132,14 +142,3 @@ int set_make_nulstr(Set *s, char **ret, size_t *ret_size) { return strv_make_nulstr(strv, ret, ret_size); } - -const char* nulstr_get(const char *nulstr, const char *needle) { - if (!nulstr) - return NULL; - - NULSTR_FOREACH(i, nulstr) - if (streq(i, needle)) - return i; - - return NULL; -} diff --git a/src/basic/nulstr-util.h b/src/basic/nulstr-util.h index d7bc5fd..d6f2f58 100644 --- a/src/basic/nulstr-util.h +++ b/src/basic/nulstr-util.h @@ -15,7 +15,6 @@ for (typeof(*(l)) *(i) = (l), *(j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) const char* nulstr_get(const char *nulstr, const char *needle); - static inline bool nulstr_contains(const char *nulstr, const char *needle) { return nulstr_get(nulstr, needle); } @@ -25,9 +24,6 @@ static inline char** strv_parse_nulstr(const char *s, size_t l) { return strv_parse_nulstr_full(s, l, false); } char** strv_split_nulstr(const char *s); -int strv_make_nulstr(char * const *l, char **p, size_t *n); -int set_make_nulstr(Set *s, char **ret, size_t *ret_size); - static inline int strv_from_nulstr(char ***ret, const char *nulstr) { char **t; @@ -40,3 +36,6 @@ static inline int strv_from_nulstr(char ***ret, const char *nulstr) { *ret = t; return 0; } + +int strv_make_nulstr(char * const *l, char **p, size_t *n); +int set_make_nulstr(Set *s, char **ret, size_t *ret_size); diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c index b4c2588..65cf3a0 100644 --- a/src/basic/ordered-set.c +++ b/src/basic/ordered-set.c @@ -91,13 +91,16 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s) { bool space = false; char *p; + assert(f); + assert(field); + if (ordered_set_isempty(s)) return; fputs(field, f); ORDERED_SET_FOREACH(p, s) - fputs_with_space(f, p, NULL, &space); + fputs_with_separator(f, p, NULL, &space); fputc('\n', f); } diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 985d89b..0d26d18 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -72,16 +72,11 @@ int path_extract_image_name(const char *path, char **ret) { 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; - } - } + /* Chop off any image suffixes we recognize (unless we already know this must refer to some dir) */ + char *m = ENDSWITH_SET(fn, ".sysext.raw", ".confext.raw", ".raw"); + if (m) + *m = 0; } /* Truncate the version/counting suffixes */ diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 0430e33..35d13eb 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -633,7 +633,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { s = *p; /* accept any number of digits, strtoull is limited to 19 */ - for (size_t i = 0; i < digits; i++,s++) { + for (size_t i = 0; i < digits; i++, s++) { if (!ascii_isdigit(*s)) { if (i == 0) return -EINVAL; @@ -691,7 +691,7 @@ int parse_ip_port(const char *s, uint16_t *ret) { return 0; } -int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) { +int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) { unsigned l, h; int r; @@ -699,7 +699,10 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) { if (r < 0) return r; - if (l <= 0 || l > 65535 || h <= 0 || h > 65535) + if (l > 65535 || h > 65535) + return -EINVAL; + + if (!allow_zero && (l == 0 || h == 0)) return -EINVAL; if (h < l) diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 1845f0a..c12988e 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -139,7 +139,7 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); -int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high); +int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero); int parse_ip_prefix_length(const char *s, int *ret); diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c index 4e3d59f..540256b 100644 --- a/src/basic/path-lookup.c +++ b/src/basic/path-lookup.c @@ -91,6 +91,37 @@ int xdg_user_data_dir(char **ret, const char *suffix) { return 1; } +int runtime_directory(char **ret, RuntimeScope scope, const char *suffix) { + int r; + + assert(ret); + assert(suffix); + assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL)); + + /* Accept $RUNTIME_DIRECTORY as authoritative + * If its missing apply the suffix to /run or $XDG_RUNTIME_DIR + * if we are in a user runtime scope. + * + * Return value indicates whether the suffix was applied or not */ + + const char *e = secure_getenv("RUNTIME_DIRECTORY"); + if (e) + return strdup_to(ret, e); + + if (scope == RUNTIME_SCOPE_USER) { + r = xdg_user_runtime_dir(ret, suffix); + if (r < 0) + return r; + } else { + char *d = path_join("/run", suffix); + if (!d) + return -ENOMEM; + *ret = d; + } + + return true; +} + static const char* const user_data_unit_paths[] = { "/usr/local/lib/systemd/user", "/usr/local/share/systemd/user", @@ -167,22 +198,16 @@ static char** user_dirs( return NULL; /* Now merge everything we found. */ - if (strv_extend(&res, persistent_control) < 0) + if (strv_extend_many( + &res, + persistent_control, + runtime_control, + transient, + generator_early, + persistent_config) < 0) return NULL; - if (strv_extend(&res, runtime_control) < 0) - return NULL; - - if (strv_extend(&res, transient) < 0) - return NULL; - - if (strv_extend(&res, generator_early) < 0) - return NULL; - - if (strv_extend(&res, persistent_config) < 0) - return NULL; - - if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) + if (strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/systemd/user") < 0) return NULL; /* global config has lower priority than the user config of the same type */ @@ -192,19 +217,15 @@ static char** user_dirs( if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0) return NULL; - if (strv_extend(&res, runtime_config) < 0) - return NULL; - - if (strv_extend(&res, global_runtime_config) < 0) - return NULL; - - if (strv_extend(&res, generator) < 0) - return NULL; - - if (strv_extend(&res, data_home) < 0) + if (strv_extend_many( + &res, + runtime_config, + global_runtime_config, + generator, + data_home) < 0) return NULL; - if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) + if (strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/systemd/user") < 0) return NULL; if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0) @@ -748,9 +769,8 @@ int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFl return r; } -void lookup_paths_free(LookupPaths *lp) { - if (!lp) - return; +void lookup_paths_done(LookupPaths *lp) { + assert(lp); lp->search_path = strv_free(lp->search_path); diff --git a/src/basic/path-lookup.h b/src/basic/path-lookup.h index 1601787..0db2c5a 100644 --- a/src/basic/path-lookup.h +++ b/src/basic/path-lookup.h @@ -59,12 +59,13 @@ int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs); int xdg_user_runtime_dir(char **ret, const char *suffix); int xdg_user_config_dir(char **ret, const char *suffix); int xdg_user_data_dir(char **ret, const char *suffix); +int runtime_directory(char **ret, RuntimeScope scope, const char *suffix); bool path_is_user_data_dir(const char *path); bool path_is_user_config_dir(const char *path); void lookup_paths_log(LookupPaths *p); -void lookup_paths_free(LookupPaths *p); +void lookup_paths_done(LookupPaths *p); char **generator_binary_paths(RuntimeScope scope); char **env_generator_binary_paths(RuntimeScope scope); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 6810bf6..068fb42 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -217,8 +217,10 @@ int path_make_relative_parent(const char *from_child, const char *to, char **ret return path_make_relative(from, to, ret); } -char* path_startswith_strv(const char *p, char **set) { - STRV_FOREACH(s, set) { +char* path_startswith_strv(const char *p, char * const *strv) { + assert(p); + + STRV_FOREACH(s, strv) { char *t; t = path_startswith(p, *s); @@ -525,6 +527,18 @@ int path_compare_filename(const char *a, const char *b) { return strcmp(fa, fb); } +int path_equal_or_inode_same_full(const char *a, const char *b, int flags) { + /* Returns true if paths are of the same entry, false if not, <0 on error. */ + + if (path_equal(a, b)) + return 1; + + if (!a || !b) + return 0; + + return inode_same(a, b, flags); +} + char* path_extend_internal(char **x, ...) { size_t sz, old_sz; char *q, *nx; @@ -684,7 +698,7 @@ int find_executable_full( * binary. */ p = getenv("PATH"); if (!p) - p = DEFAULT_PATH; + p = default_PATH(); if (exec_search_path) { STRV_FOREACH(element, exec_search_path) { @@ -1094,7 +1108,6 @@ int path_extract_filename(const char *path, char **ret) { } int path_extract_directory(const char *path, char **ret) { - _cleanup_free_ char *a = NULL; const char *c, *next = NULL; int r; @@ -1118,14 +1131,10 @@ int path_extract_directory(const char *path, char **ret) { if (*path != '/') /* filename only */ return -EDESTADDRREQ; - a = strdup("/"); - if (!a) - return -ENOMEM; - *ret = TAKE_PTR(a); - return 0; + return strdup_to(ret, "/"); } - a = strndup(path, next - path); + _cleanup_free_ char *a = strndup(path, next - path); if (!a) return -ENOMEM; @@ -1336,6 +1345,20 @@ bool dot_or_dot_dot(const char *path) { return path[2] == 0; } +bool path_implies_directory(const char *path) { + + /* Sometimes, if we look at a path we already know it must refer to a directory, because it is + * suffixed with a slash, or its last component is "." or ".." */ + + if (!path) + return false; + + if (dot_or_dot_dot(path)) + return true; + + return ENDSWITH_SET(path, "/", "/.", "/.."); +} + bool empty_or_root(const char *path) { /* For operations relative to some root directory, returns true if the specified root directory is @@ -1347,7 +1370,9 @@ bool empty_or_root(const char *path) { return path_equal(path, "/"); } -bool path_strv_contains(char **l, const char *path) { +bool path_strv_contains(char * const *l, const char *path) { + assert(path); + STRV_FOREACH(i, l) if (path_equal(*i, path)) return true; @@ -1355,7 +1380,9 @@ bool path_strv_contains(char **l, const char *path) { return false; } -bool prefixed_path_strv_contains(char **l, const char *path) { +bool prefixed_path_strv_contains(char * const *l, const char *path) { + assert(path); + STRV_FOREACH(i, l) { const char *j = *i; @@ -1363,6 +1390,7 @@ bool prefixed_path_strv_contains(char **l, const char *path) { j++; if (*j == '+') j++; + if (path_equal(j, path)) return true; } @@ -1432,3 +1460,31 @@ int path_glob_can_match(const char *pattern, const char *prefix, char **ret) { *ret = NULL; return false; } + +const char* default_PATH(void) { +#if HAVE_SPLIT_BIN + static int split = -1; + int r; + + /* Check whether /usr/sbin is not a symlink and return the appropriate $PATH. + * On error fall back to the safe value with both directories as configured… */ + + if (split < 0) + STRV_FOREACH_PAIR(bin, sbin, STRV_MAKE("/usr/bin", "/usr/sbin", + "/usr/local/bin", "/usr/local/sbin")) { + r = inode_same(*bin, *sbin, AT_NO_AUTOMOUNT); + if (r > 0 || r == -ENOENT) + continue; + if (r < 0) + log_debug_errno(r, "Failed to compare \"%s\" and \"%s\", using compat $PATH: %m", + *bin, *sbin); + split = true; + break; + } + if (split < 0) + split = false; + if (split) + return DEFAULT_PATH_WITH_SBIN; +#endif + return DEFAULT_PATH_WITHOUT_SBIN; +} diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 6d943e9..792b8ff 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -11,27 +11,26 @@ #include "strv.h" #include "time-util.h" -#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin" -#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0" +#define PATH_SPLIT_BIN(x) x "sbin:" x "bin" +#define PATH_SPLIT_BIN_NULSTR(x) x "sbin\0" x "bin\0" -#define PATH_NORMAL_SBIN_BIN(x) x "bin" -#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0" +#define PATH_MERGED_BIN(x) x "bin" +#define PATH_MERGED_BIN_NULSTR(x) x "bin\0" -#if HAVE_SPLIT_BIN -# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x) -#else -# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x) -#endif +#define DEFAULT_PATH_WITH_SBIN PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/") +#define DEFAULT_PATH_WITHOUT_SBIN PATH_MERGED_BIN("/usr/local/") ":" PATH_MERGED_BIN("/usr/") + +#define DEFAULT_PATH_COMPAT PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/") ":" PATH_SPLIT_BIN("/") -#define DEFAULT_PATH PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/") -#define DEFAULT_PATH_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/") -#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/") +const char* default_PATH(void); -#ifndef DEFAULT_USER_PATH -# define DEFAULT_USER_PATH DEFAULT_PATH +static inline const char* default_user_PATH(void) { +#ifdef DEFAULT_USER_PATH + return DEFAULT_USER_PATH; +#else + return default_PATH(); #endif +} static inline bool is_path(const char *p) { if (!p) /* A NULL pointer is definitely not a path */ @@ -68,14 +67,19 @@ static inline bool path_equal_filename(const char *a, const char *b) { return path_compare_filename(a, b) == 0; } +int path_equal_or_inode_same_full(const char *a, const char *b, int flags); static inline bool path_equal_or_inode_same(const char *a, const char *b, int flags) { - return path_equal(a, b) || inode_same(a, b, flags) > 0; + return path_equal_or_inode_same_full(a, b, flags) > 0; } char* path_extend_internal(char **x, ...); #define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX) #define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX) +static inline char* skip_leading_slash(const char *p) { + return skip_leading_chars(p, "/"); +} + typedef enum PathSimplifyFlags { PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0, } PathSimplifyFlags; @@ -101,14 +105,10 @@ static inline int path_simplify_alloc(const char *path, char **ret) { return 0; } -static inline bool path_equal_ptr(const char *a, const char *b) { - return !!a == !!b && (!a || path_equal(a, b)); -} - /* Note: the search terminates on the first NULL item. */ #define PATH_IN_SET(p, ...) path_strv_contains(STRV_MAKE(__VA_ARGS__), p) -char* path_startswith_strv(const char *p, char **set); +char* path_startswith_strv(const char *p, char * const *strv); #define PATH_STARTSWITH_SET(p, ...) path_startswith_strv(p, STRV_MAKE(__VA_ARGS__)) int path_strv_make_absolute_cwd(char **l); @@ -156,7 +156,7 @@ int fsck_exists_for_fstype(const char *fstype); char *_p, *_n; \ size_t _l; \ while (_path[0] == '/' && _path[1] == '/') \ - _path ++; \ + _path++; \ if (isempty(_root)) \ _ret = _path; \ else { \ @@ -201,6 +201,8 @@ bool valid_device_allow_pattern(const char *path); bool dot_or_dot_dot(const char *path); +bool path_implies_directory(const char *path); + static inline const char *skip_dev_prefix(const char *p) { const char *e; @@ -216,7 +218,7 @@ static inline const char* empty_to_root(const char *path) { return isempty(path) ? "/" : path; } -bool path_strv_contains(char **l, const char *path); -bool prefixed_path_strv_contains(char **l, const char *path); +bool path_strv_contains(char * const *l, const char *path); +bool prefixed_path_strv_contains(char * const *l, const char *path); int path_glob_can_match(const char *pattern, const char *prefix, char **ret); diff --git a/src/basic/pidref.c b/src/basic/pidref.c index 69b5cad..69a0102 100644 --- a/src/basic/pidref.c +++ b/src/basic/pidref.c @@ -1,12 +1,44 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#if HAVE_PIDFD_OPEN +#include <sys/pidfd.h> +#endif + #include "errno-util.h" #include "fd-util.h" #include "missing_syscall.h" +#include "missing_wait.h" #include "parse-util.h" #include "pidref.h" #include "process-util.h" #include "signal-util.h" +#include "stat-util.h" + +bool pidref_equal(const PidRef *a, const PidRef *b) { + int r; + + if (pidref_is_set(a)) { + if (!pidref_is_set(b)) + return false; + + if (a->pid != b->pid) + return false; + + if (a->fd < 0 || b->fd < 0) + return true; + + /* pidfds live in their own pidfs and each process comes with a unique inode number since + * kernel 6.8. We can safely do this on older kernels too though, as previously anonymous + * inode was used and inode number was the same for all pidfds. */ + r = fd_inode_same(a->fd, b->fd); + if (r < 0) + log_debug_errno(r, "Failed to check whether pidfds for pid " PID_FMT " are equal, assuming yes: %m", + a->pid); + return r != 0; + } + + return !pidref_is_set(b); +} int pidref_set_pid(PidRef *pidref, pid_t pid) { int fd; @@ -22,7 +54,7 @@ int pidref_set_pid(PidRef *pidref, pid_t pid) { if (fd < 0) { /* Graceful fallback in case the kernel doesn't support pidfds or is out of fds */ if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno) && !ERRNO_IS_RESOURCE(errno)) - return -errno; + return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid); fd = -EBADF; } @@ -106,6 +138,38 @@ int pidref_set_pidfd_consume(PidRef *pidref, int fd) { return r; } +int pidref_set_parent(PidRef *ret) { + _cleanup_(pidref_done) PidRef parent = PIDREF_NULL; + pid_t ppid; + int r; + + assert(ret); + + /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and + * we get reparented to other processes, with our old parent's PID already being recycled. */ + + ppid = getppid(); + for (;;) { + r = pidref_set_pid(&parent, ppid); + if (r < 0) + return r; + + if (parent.fd < 0) /* If pidfds are not available, then we are done */ + break; + + pid_t now_ppid = getppid(); + if (now_ppid == ppid) /* If our ppid is still the same, then we are done */ + break; + + /* Otherwise let's try again with the new ppid */ + ppid = now_ppid; + pidref_done(&parent); + } + + *ret = TAKE_PIDREF(parent); + return 0; +} + void pidref_done(PidRef *pidref) { assert(pidref); @@ -123,11 +187,11 @@ PidRef *pidref_free(PidRef *pidref) { return mfree(pidref); } -int pidref_dup(const PidRef *pidref, PidRef **ret) { +int pidref_copy(const PidRef *pidref, PidRef *dest) { _cleanup_close_ int dup_fd = -EBADF; pid_t dup_pid = 0; - assert(ret); + assert(dest); /* Allocates a new PidRef on the heap, making it a copy of the specified pidref. This does not try to * acquire a pidfd if we don't have one yet! @@ -150,21 +214,34 @@ int pidref_dup(const PidRef *pidref, PidRef **ret) { dup_pid = pidref->pid; } - PidRef *dup_pidref = new(PidRef, 1); - if (!dup_pidref) - return -ENOMEM; - - *dup_pidref = (PidRef) { + *dest = (PidRef) { .fd = TAKE_FD(dup_fd), .pid = dup_pid, }; + return 0; +} + +int pidref_dup(const PidRef *pidref, PidRef **ret) { + _cleanup_(pidref_freep) PidRef *dup_pidref = NULL; + int r; + + assert(ret); + + dup_pidref = newdup(PidRef, &PIDREF_NULL, 1); + if (!dup_pidref) + return -ENOMEM; + + r = pidref_copy(pidref, dup_pidref); + if (r < 0) + return r; + *ret = TAKE_PTR(dup_pidref); return 0; } int pidref_new_from_pid(pid_t pid, PidRef **ret) { - _cleanup_(pidref_freep) PidRef *n = 0; + _cleanup_(pidref_freep) PidRef *n = NULL; int r; assert(ret); @@ -270,8 +347,46 @@ bool pidref_is_self(const PidRef *pidref) { return pidref->pid == getpid_cached(); } +int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) { + int r; + + if (!pidref_is_set(pidref)) + return -ESRCH; + + if (pidref->pid == 1 || pidref->pid == getpid_cached()) + return -ECHILD; + + siginfo_t si = {}; + + if (pidref->fd >= 0) { + r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options)); + if (r >= 0) { + if (ret) + *ret = si; + return r; + } + if (r != -EINVAL) /* P_PIDFD was added in kernel 5.4 only */ + return r; + } + + r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options)); + if (r >= 0 && ret) + *ret = si; + return r; +} + +int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret) { + int r; + + for (;;) { + r = pidref_wait(pidref, ret, WEXITED); + if (r != -EINTR) + return r; + } +} + static void pidref_hash_func(const PidRef *pidref, struct siphash *state) { - siphash24_compress(&pidref->pid, sizeof(pidref->pid), state); + siphash24_compress_typesafe(pidref->pid, state); } static int pidref_compare_func(const PidRef *a, const PidRef *b) { diff --git a/src/basic/pidref.h b/src/basic/pidref.h index dada069..9920ebb 100644 --- a/src/basic/pidref.h +++ b/src/basic/pidref.h @@ -19,17 +19,7 @@ static inline bool pidref_is_set(const PidRef *pidref) { return pidref && pidref->pid > 0; } -static inline bool pidref_equal(const PidRef *a, const PidRef *b) { - - if (pidref_is_set(a)) { - if (!pidref_is_set(b)) - return false; - - return a->pid == b->pid; - } - - return !pidref_is_set(b); -} +bool pidref_equal(const PidRef *a, const PidRef *b); /* This turns a pid_t into a PidRef structure, and acquires a pidfd for it, if possible. (As opposed to * PIDREF_MAKE_FROM_PID() above, which does not acquire a pidfd.) */ @@ -38,7 +28,7 @@ int pidref_set_pidstr(PidRef *pidref, const char *pid); int pidref_set_pidfd(PidRef *pidref, int fd); int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/ int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */ - +int pidref_set_parent(PidRef *ret); static inline int pidref_set_self(PidRef *pidref) { return pidref_set_pid(pidref, 0); } @@ -49,13 +39,26 @@ void pidref_done(PidRef *pidref); PidRef *pidref_free(PidRef *pidref); DEFINE_TRIVIAL_CLEANUP_FUNC(PidRef*, pidref_free); +int pidref_copy(const PidRef *pidref, PidRef *dest); int pidref_dup(const PidRef *pidref, PidRef **ret); int pidref_new_from_pid(pid_t pid, PidRef **ret); int pidref_kill(const PidRef *pidref, int sig); int pidref_kill_and_sigcont(const PidRef *pidref, int sig); -int pidref_sigqueue(const PidRef *pidfref, int sig, int value); +int pidref_sigqueue(const PidRef *pidref, int sig, int value); + +int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options); +int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret); + +static inline void pidref_done_sigkill_wait(PidRef *pidref) { + if (!pidref_is_set(pidref)) + return; + + (void) pidref_kill(pidref, SIGKILL); + (void) pidref_wait_for_terminate(pidref, NULL); + pidref_done(pidref); +} int pidref_verify(const PidRef *pidref); diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index 522d8de..ce1ba3a 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -116,16 +116,8 @@ int proc_cmdline(char **ret) { /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */ e = secure_getenv("SYSTEMD_PROC_CMDLINE"); - if (e) { - char *m; - - m = strdup(e); - if (!m) - return -ENOMEM; - - *ret = m; - return 0; - } + if (e) + return strdup_to(ret, e); if (detect_container() > 0) return pid_get_cmdline(1, SIZE_MAX, 0, ret); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 4492e7d..c9d968d 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "architecture.h" #include "argv-util.h" +#include "cgroup-util.h" #include "dirent-util.h" #include "env-file.h" #include "env-util.h" @@ -510,7 +511,7 @@ static int get_process_link_contents(pid_t pid, const char *proc_file, char **re p = procfs_file_alloca(pid, proc_file); r = readlink_malloc(p, ret); - return r == -ENOENT ? -ESRCH : r; + return (r == -ENOENT && proc_mounted() > 0) ? -ESRCH : r; } int get_process_exe(pid_t pid, char **ret) { @@ -730,6 +731,82 @@ int get_process_ppid(pid_t pid, pid_t *ret) { return 0; } +int pid_get_start_time(pid_t pid, uint64_t *ret) { + _cleanup_free_ char *line = NULL; + const char *p; + int r; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "stat"); + r = read_one_line_file(p, &line); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its + * value, so let's skip over it manually */ + + p = strrchr(line, ')'); + if (!p) + return -EIO; + + p++; + + unsigned long llu; + + if (sscanf(p, " " + "%*c " /* state */ + "%*u " /* ppid */ + "%*u " /* pgrp */ + "%*u " /* session */ + "%*u " /* tty_nr */ + "%*u " /* tpgid */ + "%*u " /* flags */ + "%*u " /* minflt */ + "%*u " /* cminflt */ + "%*u " /* majflt */ + "%*u " /* cmajflt */ + "%*u " /* utime */ + "%*u " /* stime */ + "%*u " /* cutime */ + "%*u " /* cstime */ + "%*i " /* priority */ + "%*i " /* nice */ + "%*u " /* num_threads */ + "%*u " /* itrealvalue */ + "%lu ", /* starttime */ + &llu) != 1) + return -EIO; + + if (ret) + *ret = llu; + + return 0; +} + +int pidref_get_start_time(const PidRef *pid, uint64_t *ret) { + uint64_t t; + int r; + + if (!pidref_is_set(pid)) + return -ESRCH; + + r = pid_get_start_time(pid->pid, ret ? &t : NULL); + if (r < 0) + return r; + + r = pidref_verify(pid); + if (r < 0) + return r; + + if (ret) + *ret = t; + + return 0; +} + int get_process_umask(pid_t pid, mode_t *ret) { _cleanup_free_ char *m = NULL; const char *p; @@ -946,31 +1023,16 @@ int kill_and_sigcont(pid_t pid, int sig) { int getenv_for_pid(pid_t pid, const char *field, char **ret) { _cleanup_fclose_ FILE *f = NULL; - char *value = NULL; const char *path; - size_t l, sum = 0; + size_t sum = 0; int r; assert(pid >= 0); assert(field); assert(ret); - if (pid == 0 || pid == getpid_cached()) { - const char *e; - - e = getenv(field); - if (!e) { - *ret = NULL; - return 0; - } - - value = strdup(e); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } + if (pid == 0 || pid == getpid_cached()) + return strdup_to_full(ret, getenv(field)); if (!pid_is_valid(pid)) return -EINVAL; @@ -983,9 +1045,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) { if (r < 0) return r; - l = strlen(field); for (;;) { _cleanup_free_ char *line = NULL; + const char *match; if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */ return -ENOBUFS; @@ -998,14 +1060,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) { sum += r; - if (strneq(line, field, l) && line[l] == '=') { - value = strdup(line + l + 1); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } + match = startswith(line, field); + if (match && *match == '=') + return strdup_to_full(ret, match + 1); } *ret = NULL; @@ -1112,8 +1169,10 @@ int pidref_is_alive(const PidRef *pidref) { return -ESRCH; result = pid_is_alive(pidref->pid); - if (result < 0) + if (result < 0) { + assert(result != -ESRCH); return result; + } r = pidref_verify(pidref); if (r == -ESRCH) @@ -1224,7 +1283,7 @@ int opinionated_personality(unsigned long *ret) { if (current < 0) return current; - if (((unsigned long) current & 0xffff) == PER_LINUX32) + if (((unsigned long) current & OPINIONATED_PERSONALITY_MASK) == PER_LINUX32) *ret = PER_LINUX32; else *ret = PER_LINUX; @@ -1389,7 +1448,7 @@ static int fork_flags_to_signal(ForkFlags flags) { int safe_fork_full( const char *name, const int stdio_fds[3], - const int except_fds[], + int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid) { @@ -1462,10 +1521,11 @@ int safe_fork_full( } } - if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0) + if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0) pid = raw_clone(SIGCHLD| (FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) | - (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0)); + (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) | + (FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0)); else pid = fork(); if (pid < 0) @@ -1589,6 +1649,9 @@ int safe_fork_full( log_full_errno(prio, r, "Failed to rearrange stdio fds: %m"); _exit(EXIT_FAILURE); } + + /* Turn off O_NONBLOCK on the fdio fds, in case it was left on */ + stdio_disable_nonblock(); } else { r = make_null_stdio(); if (r < 0) { @@ -1614,6 +1677,19 @@ int safe_fork_full( } } + if (flags & FORK_PACK_FDS) { + /* FORK_CLOSE_ALL_FDS ensures that except_fds are the only FDs >= 3 that are + * open, this is including the log. This is required by pack_fds, which will + * get stuck in an infinite loop of any FDs other than except_fds are open. */ + assert(FLAGS_SET(flags, FORK_CLOSE_ALL_FDS)); + + r = pack_fds(except_fds, n_except_fds); + if (r < 0) { + log_full_errno(prio, r, "Failed to pack file descriptors: %m"); + _exit(EXIT_FAILURE); + } + } + if (flags & FORK_CLOEXEC_OFF) { r = fd_cloexec_many(except_fds, n_except_fds, false); if (r < 0) { @@ -1650,10 +1726,34 @@ int safe_fork_full( return 0; } +int pidref_safe_fork_full( + const char *name, + const int stdio_fds[3], + int except_fds[], + size_t n_except_fds, + ForkFlags flags, + PidRef *ret_pid) { + + pid_t pid; + int r, q; + + assert(!FLAGS_SET(flags, FORK_WAIT)); + + r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid); + if (r < 0) + return r; + + q = pidref_set_pid(ret_pid, pid); + if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */ + *ret_pid = PIDREF_MAKE_FROM_PID(pid); + + return r; +} + int namespace_fork( const char *outer_name, const char *inner_name, - const int except_fds[], + int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, @@ -1927,47 +2027,115 @@ int make_reaper_process(bool b) { return 0; } -int posix_spawn_wrapper(const char *path, char *const *argv, char *const *envp, pid_t *ret_pid) { +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(posix_spawnattr_t*, posix_spawnattr_destroy, NULL); + +int posix_spawn_wrapper( + const char *path, + char * const *argv, + char * const *envp, + const char *cgroup, + PidRef *ret_pidref) { + + short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF; posix_spawnattr_t attr; sigset_t mask; - pid_t pid; int r; /* Forks and invokes 'path' with 'argv' and 'envp' using CLONE_VM and CLONE_VFORK, which means the * caller will be blocked until the child either exits or exec's. The memory of the child will be * fully shared with the memory of the parent, so that there are no copy-on-write or memory.max - * issues. */ + * issues. + * + * Also, move the newly-created process into 'cgroup' through POSIX_SPAWN_SETCGROUP (clone3()) + * if available. Note that CLONE_INTO_CGROUP is only supported on cgroup v2. + * returns 1: We're already in the right cgroup + * 0: 'cgroup' not specified or POSIX_SPAWN_SETCGROUP is not supported. The caller + * needs to call 'cg_attach' on their own */ assert(path); assert(argv); - assert(ret_pid); + assert(ret_pidref); assert_se(sigfillset(&mask) >= 0); r = posix_spawnattr_init(&attr); if (r != 0) return -r; /* These functions return a positive errno on failure */ - /* Set all signals to SIG_DFL */ - r = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF); + + /* Initialization needs to succeed before we can set up a destructor. */ + _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr; + +#if HAVE_PIDFD_SPAWN + _cleanup_close_ int cgroup_fd = -EBADF; + + if (cgroup) { + _cleanup_free_ char *resolved_cgroup = NULL; + + r = cg_get_path_and_check( + SYSTEMD_CGROUP_CONTROLLER, + cgroup, + /* suffix= */ NULL, + &resolved_cgroup); + if (r < 0) + return r; + + cgroup_fd = open(resolved_cgroup, O_PATH|O_DIRECTORY|O_CLOEXEC); + if (cgroup_fd < 0) + return -errno; + + r = posix_spawnattr_setcgroup_np(&attr, cgroup_fd); + if (r != 0) + return -r; + + flags |= POSIX_SPAWN_SETCGROUP; + } +#endif + + r = posix_spawnattr_setflags(&attr, flags); if (r != 0) - goto fail; + return -r; r = posix_spawnattr_setsigmask(&attr, &mask); if (r != 0) - goto fail; + return -r; - r = posix_spawn(&pid, path, NULL, &attr, argv, envp); +#if HAVE_PIDFD_SPAWN + _cleanup_close_ int pidfd = -EBADF; + + r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp); + if (r == 0) { + r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd)); + if (r < 0) + return r; + + return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP); + } + if (ERRNO_IS_NOT_SUPPORTED(r)) { + /* clone3() could also return EOPNOTSUPP if the target cgroup is in threaded mode. */ + if (cgroup && cg_is_threaded(cgroup) > 0) + return -EUCLEAN; + + /* clone3() not available? */ + } else if (!ERRNO_IS_PRIVILEGE(r)) + return -r; + + /* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the + * flags to remove the cgroup one, which is what redirects to clone3() */ + flags &= ~POSIX_SPAWN_SETCGROUP; + r = posix_spawnattr_setflags(&attr, flags); if (r != 0) - goto fail; + return -r; +#endif - *ret_pid = pid; + pid_t pid; + r = posix_spawn(&pid, path, NULL, &attr, argv, envp); + if (r != 0) + return -r; - posix_spawnattr_destroy(&attr); - return 0; + r = pidref_set_pid(ret_pidref, pid); + if (r < 0) + return r; -fail: - assert(r > 0); - posix_spawnattr_destroy(&attr); - return -r; + return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */ } int proc_dir_open(DIR **ret) { diff --git a/src/basic/process-util.h b/src/basic/process-util.h index af6cba1..8308402 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -54,6 +54,8 @@ int get_process_cwd(pid_t pid, char **ret); int get_process_root(pid_t pid, char **ret); int get_process_environ(pid_t pid, char **ret); int get_process_ppid(pid_t pid, pid_t *ret); +int pid_get_start_time(pid_t pid, uint64_t *ret); +int pidref_get_start_time(const PidRef* pid, uint64_t *ret); int get_process_umask(pid_t pid, mode_t *ret); int container_get_leader(const char *machine, pid_t *pid); @@ -99,12 +101,17 @@ bool is_main_thread(void); bool oom_score_adjust_is_valid(int oa); #ifndef PERSONALITY_INVALID -/* personality(7) documents that 0xffffffffUL is used for querying the +/* personality(2) documents that 0xFFFFFFFFUL is used for querying the * current personality, hence let's use that here as error * indicator. */ -#define PERSONALITY_INVALID 0xffffffffLU +#define PERSONALITY_INVALID 0xFFFFFFFFUL #endif +/* The personality() syscall returns a 32-bit value where the top three bytes are reserved for flags that + * emulate historical or architectural quirks, and only the least significant byte reflects the actual + * personality we're interested in. */ +#define OPINIONATED_PERSONALITY_MASK 0xFFUL + unsigned long personality_from_string(const char *p); const char *personality_to_string(unsigned long); @@ -152,11 +159,11 @@ int must_be_root(void); pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata); -/* 💣 Note that FORK_NEW_USERNS + FORK_NEW_MOUNTNS should not be called in threaded programs, because they - * cause us to use raw_clone() which does not synchronize the glibc malloc() locks, and thus will cause - * deadlocks if the parent uses threads and the child does memory allocations. Hence: if the parent is - * threaded these flags may not be used. These flags cannot be used if the parent uses threads or the child - * uses malloc(). 💣 */ +/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, or FORK_NEW_NETNS should not be called in threaded + * programs, because they cause us to use raw_clone() which does not synchronize the glibc malloc() locks, + * and thus will cause deadlocks if the parent uses threads and the child does memory allocations. Hence: if + * the parent is threaded these flags may not be used. These flags cannot be used if the parent uses threads + * or the child uses malloc(). 💣 */ typedef enum ForkFlags { FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ @@ -177,12 +184,14 @@ typedef enum ForkFlags { FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */ FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */ FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */ + FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ + FORK_PACK_FDS = 1 << 20, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */ } ForkFlags; int safe_fork_full( const char *name, const int stdio_fds[3], - const int except_fds[], + int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); @@ -191,7 +200,30 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid); } -int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid); +int pidref_safe_fork_full( + const char *name, + const int stdio_fds[3], + int except_fds[], + size_t n_except_fds, + ForkFlags flags, + PidRef *ret_pid); + +static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) { + return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid); +} + +int namespace_fork( + const char *outer_name, + const char *inner_name, + int except_fds[], + size_t n_except_fds, + ForkFlags flags, + int pidns_fd, + int mntns_fd, + int netns_fd, + int userns_fd, + int root_fd, + pid_t *ret_pid); int set_oom_score_adjust(int value); int get_oom_score_adjust(int *ret); @@ -223,7 +255,12 @@ int get_process_threads(pid_t pid); int is_reaper_process(void); int make_reaper_process(bool b); -int posix_spawn_wrapper(const char *path, char *const *argv, char *const *envp, pid_t *ret_pid); +int posix_spawn_wrapper( + const char *path, + char * const *argv, + char * const *envp, + const char *cgroup, + PidRef *ret_pidref); int proc_dir_open(DIR **ret); int proc_dir_read(DIR *d, pid_t *ret); diff --git a/src/basic/recurse-dir.c b/src/basic/recurse-dir.c index 5e98b7a..7767331 100644 --- a/src/basic/recurse-dir.c +++ b/src/basic/recurse-dir.c @@ -4,6 +4,7 @@ #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "missing_syscall.h" #include "mountpoint-util.h" #include "recurse-dir.h" @@ -132,6 +133,18 @@ int readdir_all(int dir_fd, return 0; } +int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret) { + _cleanup_close_ int dir_fd = -EBADF; + + assert(fd >= 0 || fd == AT_FDCWD); + + dir_fd = xopenat(fd, path, O_DIRECTORY|O_CLOEXEC); + if (dir_fd < 0) + return dir_fd; + + return readdir_all(dir_fd, flags, ret); +} + int recurse_dir( int dir_fd, const char *path, diff --git a/src/basic/recurse-dir.h b/src/basic/recurse-dir.h index 9f6a7ad..aaeae95 100644 --- a/src/basic/recurse-dir.h +++ b/src/basic/recurse-dir.h @@ -76,6 +76,7 @@ typedef struct DirectoryEntries { } DirectoryEntries; int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret); +int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret); int recurse_dir(int dir_fd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata); int recurse_dir_at(int atfd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata); diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index c1f0b2b..a9f7b87 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -6,11 +6,14 @@ #include "errno-util.h" #include "extract-word.h" #include "fd-util.h" +#include "fileio.h" #include "format-util.h" #include "macro.h" #include "missing_resource.h" +#include "process-util.h" #include "rlimit-util.h" #include "string-table.h" +#include "strv.h" #include "time-util.h" int setrlimit_closest(int resource, const struct rlimit *rlim) { @@ -426,3 +429,116 @@ int rlimit_nofile_safe(void) { return 1; } + +int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret) { + + static const char * const prefix_table[_RLIMIT_MAX] = { + [RLIMIT_CPU] = "Max cpu time", + [RLIMIT_FSIZE] = "Max file size", + [RLIMIT_DATA] = "Max data size", + [RLIMIT_STACK] = "Max stack size", + [RLIMIT_CORE] = "Max core file size", + [RLIMIT_RSS] = "Max resident set", + [RLIMIT_NPROC] = "Max processes", + [RLIMIT_NOFILE] = "Max open files", + [RLIMIT_MEMLOCK] = "Max locked memory", + [RLIMIT_AS] = "Max address space", + [RLIMIT_LOCKS] = "Max file locks", + [RLIMIT_SIGPENDING] = "Max pending signals", + [RLIMIT_MSGQUEUE] = "Max msgqueue size", + [RLIMIT_NICE] = "Max nice priority", + [RLIMIT_RTPRIO] = "Max realtime priority", + [RLIMIT_RTTIME] = "Max realtime timeout", + }; + + int r; + + assert(resource >= 0); + assert(resource < _RLIMIT_MAX); + assert(pid >= 0); + assert(ret); + + if (pid == 0 || pid == getpid_cached()) + return RET_NERRNO(getrlimit(resource, ret)); + + r = RET_NERRNO(prlimit(pid, resource, /* new_limit= */ NULL, ret)); + if (!ERRNO_IS_NEG_PRIVILEGE(r)) + return r; + + /* We don't have access? Then try to go via /proc/$PID/limits. Weirdly that's world readable in + * contrast to querying the data via prlimit() */ + + const char *p = procfs_file_alloca(pid, "limits"); + _cleanup_free_ char *limits = NULL; + + r = read_full_virtual_file(p, &limits, NULL); + if (r < 0) + return -EPERM; /* propagate original permission error if we can't access the limits file */ + + _cleanup_strv_free_ char **l = NULL; + l = strv_split(limits, "\n"); + if (!l) + return -ENOMEM; + + STRV_FOREACH(i, strv_skip(l, 1)) { + _cleanup_free_ char *soft = NULL, *hard = NULL; + uint64_t sv, hv; + const char *e; + + e = startswith(*i, prefix_table[resource]); + if (!e) + continue; + + if (*e != ' ') + continue; + + e += strspn(e, WHITESPACE); + + size_t n; + n = strcspn(e, WHITESPACE); + if (n == 0) + continue; + + soft = strndup(e, n); + if (!soft) + return -ENOMEM; + + e += n; + if (*e != ' ') + continue; + + e += strspn(e, WHITESPACE); + n = strcspn(e, WHITESPACE); + if (n == 0) + continue; + + hard = strndup(e, n); + if (!hard) + return -ENOMEM; + + if (streq(soft, "unlimited")) + sv = RLIM_INFINITY; + else { + r = safe_atou64(soft, &sv); + if (r < 0) + return r; + } + + if (streq(hard, "unlimited")) + hv = RLIM_INFINITY; + else { + r = safe_atou64(hard, &hv); + if (r < 0) + return r; + } + + *ret = (struct rlimit) { + .rlim_cur = sv, + .rlim_max = hv, + }; + + return 0; + } + + return -ENOTRECOVERABLE; +} diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h index 202c3fd..afc1a1f 100644 --- a/src/basic/rlimit-util.h +++ b/src/basic/rlimit-util.h @@ -25,3 +25,5 @@ void rlimit_free_all(struct rlimit **rl); int rlimit_nofile_bump(int limit); int rlimit_nofile_safe(void); + +int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret); diff --git a/src/basic/sha256.c b/src/basic/sha256.c new file mode 100644 index 0000000..f011695 --- /dev/null +++ b/src/basic/sha256.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <unistd.h> + +#include "hexdecoct.h" +#include "macro.h" +#include "sha256.h" + +int sha256_fd(int fd, uint64_t max_size, uint8_t ret[static SHA256_DIGEST_SIZE]) { + struct sha256_ctx ctx; + uint64_t total_size = 0; + + sha256_init_ctx(&ctx); + + for (;;) { + uint8_t buffer[64 * 1024]; + ssize_t n; + + n = read(fd, buffer, sizeof(buffer)); + if (n < 0) + return -errno; + if (n == 0) + break; + + if (!INC_SAFE(&total_size, n) || total_size > max_size) + return -EFBIG; + + sha256_process_bytes(buffer, n, &ctx); + } + + sha256_finish_ctx(&ctx, ret); + return 0; +} + +int parse_sha256(const char *s, uint8_t ret[static SHA256_DIGEST_SIZE]) { + _cleanup_free_ uint8_t *data = NULL; + size_t size = 0; + int r; + + if (!sha256_is_valid(s)) + return -EINVAL; + + r = unhexmem_full(s, SHA256_DIGEST_SIZE * 2, false, (void**) &data, &size); + if (r < 0) + return r; + assert(size == SHA256_DIGEST_SIZE); + + memcpy(ret, data, size); + return 0; +} diff --git a/src/basic/sha256.h b/src/basic/sha256.h new file mode 100644 index 0000000..95bac1b --- /dev/null +++ b/src/basic/sha256.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#pragma once + +#include <stdint.h> + +#include "sha256-fundamental.h" +#include "string-util.h" + +int sha256_fd(int fd, uint64_t max_size, uint8_t ret[static SHA256_DIGEST_SIZE]); + +int parse_sha256(const char *s, uint8_t res[static SHA256_DIGEST_SIZE]); + +static inline bool sha256_is_valid(const char *s) { + return s && in_charset(s, HEXDIGITS) && (strlen(s) == SHA256_DIGEST_SIZE * 2); +} diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index 5d948462..27d094b 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -18,7 +18,7 @@ int reset_all_signal_handlers(void) { .sa_handler = SIG_DFL, .sa_flags = SA_RESTART, }; - int r = 0; + int ret = 0, r; for (int sig = 1; sig < _NSIG; sig++) { @@ -26,14 +26,14 @@ int reset_all_signal_handlers(void) { if (IN_SET(sig, SIGKILL, SIGSTOP)) continue; - /* On Linux the first two RT signals are reserved by - * glibc, and sigaction() will return EINVAL for them. */ - if (sigaction(sig, &sa, NULL) < 0) - if (errno != EINVAL && r >= 0) - r = -errno; + /* On Linux the first two RT signals are reserved by glibc, and sigaction() will return + * EINVAL for them. */ + r = RET_NERRNO(sigaction(sig, &sa, NULL)); + if (r != -EINVAL) + RET_GATHER(ret, r); } - return r; + return ret; } int reset_signal_mask(void) { @@ -57,10 +57,7 @@ int sigaction_many_internal(const struct sigaction *sa, ...) { if (sig == 0) continue; - if (sigaction(sig, sa, NULL) < 0) { - if (r >= 0) - r = -errno; - } + RET_GATHER(r, RET_NERRNO(sigaction(sig, sa, NULL))); } va_end(ap); @@ -87,7 +84,7 @@ static int sigset_add_many_ap(sigset_t *ss, va_list ap) { return r; } -int sigset_add_many(sigset_t *ss, ...) { +int sigset_add_many_internal(sigset_t *ss, ...) { va_list ap; int r; @@ -98,7 +95,7 @@ int sigset_add_many(sigset_t *ss, ...) { return r; } -int sigprocmask_many(int how, sigset_t *old, ...) { +int sigprocmask_many_internal(int how, sigset_t *old, ...) { va_list ap; sigset_t ss; int r; @@ -113,46 +110,43 @@ int sigprocmask_many(int how, sigset_t *old, ...) { if (r < 0) return r; - if (sigprocmask(how, &ss, old) < 0) - return -errno; - - return 0; + return RET_NERRNO(sigprocmask(how, &ss, old)); } static const char *const static_signal_table[] = { - [SIGHUP] = "HUP", - [SIGINT] = "INT", - [SIGQUIT] = "QUIT", - [SIGILL] = "ILL", - [SIGTRAP] = "TRAP", - [SIGABRT] = "ABRT", - [SIGBUS] = "BUS", - [SIGFPE] = "FPE", - [SIGKILL] = "KILL", - [SIGUSR1] = "USR1", - [SIGSEGV] = "SEGV", - [SIGUSR2] = "USR2", - [SIGPIPE] = "PIPE", - [SIGALRM] = "ALRM", - [SIGTERM] = "TERM", + [SIGHUP] = "HUP", + [SIGINT] = "INT", + [SIGQUIT] = "QUIT", + [SIGILL] = "ILL", + [SIGTRAP] = "TRAP", + [SIGABRT] = "ABRT", + [SIGBUS] = "BUS", + [SIGFPE] = "FPE", + [SIGKILL] = "KILL", + [SIGUSR1] = "USR1", + [SIGSEGV] = "SEGV", + [SIGUSR2] = "USR2", + [SIGPIPE] = "PIPE", + [SIGALRM] = "ALRM", + [SIGTERM] = "TERM", #ifdef SIGSTKFLT [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ #endif - [SIGCHLD] = "CHLD", - [SIGCONT] = "CONT", - [SIGSTOP] = "STOP", - [SIGTSTP] = "TSTP", - [SIGTTIN] = "TTIN", - [SIGTTOU] = "TTOU", - [SIGURG] = "URG", - [SIGXCPU] = "XCPU", - [SIGXFSZ] = "XFSZ", + [SIGCHLD] = "CHLD", + [SIGCONT] = "CONT", + [SIGSTOP] = "STOP", + [SIGTSTP] = "TSTP", + [SIGTTIN] = "TTIN", + [SIGTTOU] = "TTOU", + [SIGURG] = "URG", + [SIGXCPU] = "XCPU", + [SIGXFSZ] = "XFSZ", [SIGVTALRM] = "VTALRM", - [SIGPROF] = "PROF", - [SIGWINCH] = "WINCH", - [SIGIO] = "IO", - [SIGPWR] = "PWR", - [SIGSYS] = "SYS" + [SIGPROF] = "PROF", + [SIGWINCH] = "WINCH", + [SIGIO] = "IO", + [SIGPWR] = "PWR", + [SIGSYS] = "SYS" }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int); @@ -274,7 +268,7 @@ int pop_pending_signal_internal(int sig, ...) { if (r < 0) return r; - r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 }); + r = sigtimedwait(&ss, NULL, &(const struct timespec) {}); if (r < 0) { if (errno == EAGAIN) return 0; diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index ad2ba84..8826fbe 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -31,8 +31,11 @@ int sigaction_many_internal(const struct sigaction *sa, ...); #define sigaction_many(sa, ...) \ sigaction_many_internal(sa, __VA_ARGS__, -1) -int sigset_add_many(sigset_t *ss, ...); -int sigprocmask_many(int how, sigset_t *old, ...); +int sigset_add_many_internal(sigset_t *ss, ...); +#define sigset_add_many(...) sigset_add_many_internal(__VA_ARGS__, -1) + +int sigprocmask_many_internal(int how, sigset_t *old, ...); +#define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1) const char *signal_to_string(int i) _const_; int signal_from_string(const char *s) _pure_; @@ -46,7 +49,7 @@ static inline void block_signals_reset(sigset_t *ss) { #define BLOCK_SIGNALS(...) \ _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ sigset_t _t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \ + assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__) >= 0); \ _t; \ }) diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h index 0b3e845..2ef4a04 100644 --- a/src/basic/siphash24.h +++ b/src/basic/siphash24.h @@ -22,15 +22,16 @@ struct siphash { void siphash24_init(struct siphash *state, const uint8_t k[static 16]); void siphash24_compress(const void *in, size_t inlen, struct siphash *state); #define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) +#define siphash24_compress_typesafe(in, state) \ + siphash24_compress(&(in), sizeof(typeof(in)), (state)) static inline void siphash24_compress_boolean(bool in, struct siphash *state) { - uint8_t i = in; - - siphash24_compress(&i, sizeof i, state); + siphash24_compress_byte(in, state); } static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) { - siphash24_compress(&in, sizeof in, state); + uint64_t u = htole64(in); + siphash24_compress_typesafe(u, state); } static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) { diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index beb64d8..6e304e8 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1,9 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* Make sure the net/if.h header is included before any linux/ one */ +#include <net/if.h> #include <arpa/inet.h> #include <errno.h> #include <limits.h> -#include <net/if.h> #include <netdb.h> #include <netinet/ip.h> #include <poll.h> @@ -453,6 +454,7 @@ int sockaddr_pretty( assert(sa); assert(salen >= sizeof(sa->sa.sa_family)); + assert(ret); switch (sa->sa.sa_family) { @@ -547,7 +549,7 @@ int sockaddr_pretty( } else { if (path[path_len - 1] == '\0') /* We expect a terminating NUL and don't print it */ - path_len --; + path_len--; p = cescape_length(path, path_len); } @@ -628,29 +630,27 @@ int getsockname_pretty(int fd, char **ret) { return sockaddr_pretty(&sa.sa, salen, false, true, ret); } -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) { +int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret) { + char host[NI_MAXHOST]; int r; - char host[NI_MAXHOST], *ret; - assert(_ret); + assert(sa); + assert(salen >= sizeof(sa_family_t)); + assert(ret); - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS); + r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS); if (r != 0) { - int saved_errno = errno; - - r = sockaddr_pretty(&sa->sa, salen, true, true, &ret); - if (r < 0) - return r; + if (r == EAI_MEMORY) + return log_oom_debug(); + if (r == EAI_SYSTEM) + log_debug_errno(errno, "getnameinfo() failed, ignoring: %m"); + else + log_debug("getnameinfo() failed, ignoring: %s", gai_strerror(r)); - log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); - } else { - ret = strdup(host); - if (!ret) - return -ENOMEM; + return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret); } - *_ret = ret; - return 0; + return strdup_to(ret, host); } static const char* const netlink_family_table[] = { @@ -872,13 +872,11 @@ bool address_label_valid(const char *p) { int getpeercred(int fd, struct ucred *ucred) { socklen_t n = sizeof(struct ucred); struct ucred u; - int r; assert(fd >= 0); assert(ucred); - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n); - if (r < 0) + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0) return -errno; if (n != sizeof(struct ucred)) @@ -907,8 +905,10 @@ int getpeersec(int fd, char **ret) { if (!s) return -ENOMEM; - if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) + if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) { + s[n] = 0; break; + } if (errno != ERANGE) return -errno; @@ -925,12 +925,16 @@ int getpeersec(int fd, char **ret) { } int getpeergroups(int fd, gid_t **ret) { - socklen_t n = sizeof(gid_t) * 64; + socklen_t n = sizeof(gid_t) * 64U; _cleanup_free_ gid_t *d = NULL; assert(fd >= 0); assert(ret); + long ngroups_max = sysconf(_SC_NGROUPS_MAX); + if (ngroups_max > 0) + n = MAX(n, sizeof(gid_t) * (socklen_t) ngroups_max); + for (;;) { d = malloc(n); if (!d) @@ -948,7 +952,7 @@ int getpeergroups(int fd, gid_t **ret) { assert_se(n % sizeof(gid_t) == 0); n /= sizeof(gid_t); - if ((socklen_t) (int) n != n) + if (n > INT_MAX) return -E2BIG; *ret = TAKE_PTR(d); @@ -956,6 +960,21 @@ int getpeergroups(int fd, gid_t **ret) { return (int) n; } +int getpeerpidfd(int fd) { + socklen_t n = sizeof(int); + int pidfd = -EBADF; + + assert(fd >= 0); + + if (getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &pidfd, &n) < 0) + return -errno; + + if (n != sizeof(int)) + return -EIO; + + return pidfd; +} + ssize_t send_many_fds_iov_sa( int transport_fd, int *fds_array, size_t n_fds_array, @@ -1093,14 +1112,10 @@ ssize_t receive_many_fds_iov( if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); - fds_array = GREEDY_REALLOC(fds_array, n_fds_array + n); - if (!fds_array) { + if (!GREEDY_REALLOC_APPEND(fds_array, n_fds_array, CMSG_TYPED_DATA(cmsg, int), n)) { cmsg_close_all(&mh); return -ENOMEM; } - - memcpy(fds_array + n_fds_array, CMSG_TYPED_DATA(cmsg, int), sizeof(int) * n); - n_fds_array += n; } if (n_fds_array == 0) { @@ -1641,6 +1656,50 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s) { return 0; } +int vsock_parse_port(const char *s, unsigned *ret) { + int r; + + assert(ret); + + if (!s) + return -EINVAL; + + unsigned u; + r = safe_atou(s, &u); + if (r < 0) + return r; + + /* Port 0 is apparently valid and not special in AF_VSOCK (unlike on IP). But VMADDR_PORT_ANY + * (UINT32_MAX) is. Hence refuse that. */ + + if (u == VMADDR_PORT_ANY) + return -EINVAL; + + *ret = u; + return 0; +} + +int vsock_parse_cid(const char *s, unsigned *ret) { + assert(ret); + + if (!s) + return -EINVAL; + + /* Parsed an AF_VSOCK "CID". This is a 32bit entity, and the usual type is "unsigned". We recognize + * the three special CIDs as strings, and otherwise parse the numeric CIDs. */ + + if (streq(s, "hypervisor")) + *ret = VMADDR_CID_HYPERVISOR; + else if (streq(s, "local")) + *ret = VMADDR_CID_LOCAL; + else if (streq(s, "host")) + *ret = VMADDR_CID_HOST; + else + return safe_atou(s, ret); + + return 0; +} + int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { /* AF_VSOCK socket in vsock:cid:port notation */ _cleanup_free_ char *n = NULL; @@ -1666,7 +1725,7 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { if (!e) return -EINVAL; - r = safe_atou(e+1, &port); + r = vsock_parse_port(e+1, &port); if (r < 0) return r; @@ -1677,15 +1736,15 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { if (isempty(n)) cid = VMADDR_CID_ANY; else { - r = safe_atou(n, &cid); + r = vsock_parse_cid(n, &cid); if (r < 0) return r; } *ret_address = (SocketAddress) { .sockaddr.vm = { - .svm_cid = cid, .svm_family = AF_VSOCK, + .svm_cid = cid, .svm_port = port, }, .type = type, @@ -1694,3 +1753,18 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { return 0; } + +int vsock_get_local_cid(unsigned *ret) { + _cleanup_close_ int vsock_fd = -EBADF; + + assert(ret); + + vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC); + if (vsock_fd < 0) + return log_debug_errno(errno, "Failed to open /dev/vsock: %m"); + + if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret) < 0) + return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m"); + + return 0; +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 9a11df8..c784125 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -113,7 +113,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ int getpeername_pretty(int fd, bool include_port, char **ret); int getsockname_pretty(int fd, char **ret); -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); +int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **_ret); const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; @@ -152,6 +152,7 @@ bool address_label_valid(const char *p); int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); int getpeergroups(int fd, gid_t **ret); +int getpeerpidfd(int fd); ssize_t send_many_fds_iov_sa( int transport_fd, @@ -373,6 +374,14 @@ int socket_get_mtu(int fd, int af, size_t *ret); int connect_unix_path(int fd, int dir_fd, const char *path); +static inline bool VSOCK_CID_IS_REGULAR(unsigned cid) { + /* 0, 1, 2, UINT32_MAX are special, refuse those */ + return cid > 2 && cid < UINT32_MAX; +} + +int vsock_parse_port(const char *s, unsigned *ret); +int vsock_parse_cid(const char *s, unsigned *ret); + /* Parses AF_UNIX and AF_VSOCK addresses. AF_INET[6] require some netlink calls, so it cannot be in * src/basic/ and is done from 'socket_local_address from src/shared/. Return -EPROTO in case of * protocol mismatch. */ @@ -385,3 +394,5 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s); * /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file * authoritative. */ #define SOMAXCONN_DELUXE INT_MAX + +int vsock_get_local_cid(unsigned *ret); diff --git a/src/basic/special.h b/src/basic/special.h index a625e75..166737a 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -47,6 +47,7 @@ #define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */ #define SPECIAL_TIME_SET_TARGET "time-set.target" #define SPECIAL_BASIC_TARGET "basic.target" +#define SPECIAL_TPM2_TARGET "tpm2.target" /* LSB compatibility */ #define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ @@ -83,8 +84,10 @@ #define SPECIAL_FSCK_SERVICE "systemd-fsck@.service" #define SPECIAL_FSCK_ROOT_SERVICE "systemd-fsck-root.service" #define SPECIAL_FSCK_USR_SERVICE "systemd-fsck-usr.service" -#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service" -#define SPECIAL_QUOTAON_SERVICE "quotaon.service" +#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck@.service" +#define SPECIAL_QUOTACHECK_ROOT_SERVICE "systemd-quotacheck-root.service" +#define SPECIAL_QUOTAON_SERVICE "quotaon@.service" +#define SPECIAL_QUOTAON_ROOT_SERVICE "quotaon-root.service" #define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" #define SPECIAL_VOLATILE_ROOT_SERVICE "systemd-volatile-root.service" #define SPECIAL_UDEVD_SERVICE "systemd-udevd.service" diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 581370d..a833aa2 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -25,43 +25,130 @@ #include "stat-util.h" #include "string-util.h" -int is_symlink(const char *path) { - struct stat info; +static int verify_stat_at( + int fd, + const char *path, + bool follow, + int (*verify_func)(const struct stat *st), + bool verify) { - assert(path); + struct stat st; + int r; + + assert(fd >= 0 || fd == AT_FDCWD); + assert(!isempty(path) || !follow); + assert(verify_func); - if (lstat(path, &info) < 0) + if (fstatat(fd, strempty(path), &st, + (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0) return -errno; - return !!S_ISLNK(info.st_mode); + r = verify_func(&st); + return verify ? r : r >= 0; } -int is_dir_full(int atfd, const char* path, bool follow) { - struct stat st; - int r; +int stat_verify_regular(const struct stat *st) { + assert(st); - assert(atfd >= 0 || atfd == AT_FDCWD); - assert(atfd >= 0 || path); + /* Checks whether the specified stat() structure refers to a regular file. If not returns an + * appropriate error code. */ - if (path) - r = fstatat(atfd, path, &st, follow ? 0 : AT_SYMLINK_NOFOLLOW); - else - r = fstat(atfd, &st); - if (r < 0) - return -errno; + if (S_ISDIR(st->st_mode)) + return -EISDIR; + + if (S_ISLNK(st->st_mode)) + return -ELOOP; + + if (!S_ISREG(st->st_mode)) + return -EBADFD; - return !!S_ISDIR(st.st_mode); + return 0; } -int is_device_node(const char *path) { - struct stat info; +int verify_regular_at(int fd, const char *path, bool follow) { + return verify_stat_at(fd, path, follow, stat_verify_regular, true); +} - assert(path); +int fd_verify_regular(int fd) { + assert(fd >= 0); + return verify_regular_at(fd, NULL, false); +} - if (lstat(path, &info) < 0) - return -errno; +int stat_verify_directory(const struct stat *st) { + assert(st); + + if (S_ISLNK(st->st_mode)) + return -ELOOP; + + if (!S_ISDIR(st->st_mode)) + return -ENOTDIR; + + return 0; +} + +int fd_verify_directory(int fd) { + assert(fd >= 0); + return verify_stat_at(fd, NULL, false, stat_verify_directory, true); +} - return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); +int is_dir_at(int fd, const char *path, bool follow) { + return verify_stat_at(fd, path, follow, stat_verify_directory, false); +} + +int is_dir(const char *path, bool follow) { + assert(!isempty(path)); + return is_dir_at(AT_FDCWD, path, follow); +} + +int stat_verify_symlink(const struct stat *st) { + assert(st); + + if (S_ISDIR(st->st_mode)) + return -EISDIR; + + if (!S_ISLNK(st->st_mode)) + return -ENOLINK; + + return 0; +} + +int is_symlink(const char *path) { + assert(!isempty(path)); + return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false); +} + +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) { + assert(fd >= 0); + return verify_stat_at(fd, NULL, false, stat_verify_linked, true); +} + +int stat_verify_device_node(const struct stat *st) { + assert(st); + + if (S_ISLNK(st->st_mode)) + return -ELOOP; + + if (S_ISDIR(st->st_mode)) + return -EISDIR; + + if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode)) + return -ENOTTY; + + return 0; +} + +int is_device_node(const char *path) { + assert(!isempty(path)); + return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false); } int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) { @@ -142,7 +229,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) { * When looking under root_dir, we can't expect /dev/ to be mounted, * so let's see if the path is a (possibly dangling) symlink to /dev/null. */ - if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null")) + if (path_equal(path_startswith(fn, root ?: "/"), "dev/null")) return true; r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st); @@ -152,7 +239,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) { return null_or_empty(&st); } -static int fd_is_read_only_fs(int fd) { +int fd_is_read_only_fs(int fd) { struct statvfs st; assert(fd >= 0); @@ -187,14 +274,12 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl struct stat a, b; assert(fda >= 0 || fda == AT_FDCWD); - assert(filea); assert(fdb >= 0 || fdb == AT_FDCWD); - assert(fileb); - if (fstatat(fda, filea, &a, flags) < 0) + if (fstatat(fda, strempty(filea), &a, flags) < 0) return log_debug_errno(errno, "Cannot stat %s: %m", filea); - if (fstatat(fdb, fileb, &b, flags) < 0) + if (fstatat(fdb, strempty(fileb), &b, flags) < 0) return log_debug_errno(errno, "Cannot stat %s: %m", fileb); return stat_inode_same(&a, &b); @@ -262,90 +347,6 @@ 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. */ - - if (S_ISDIR(st->st_mode)) - return -EISDIR; - - if (S_ISLNK(st->st_mode)) - return -ELOOP; - - if (!S_ISREG(st->st_mode)) - return -EBADFD; - - return 0; -} - -int fd_verify_regular(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return stat_verify_regular(&st); -} - -int verify_regular_at(int dir_fd, const char *path, bool follow) { - struct stat st; - - assert(dir_fd >= 0 || dir_fd == AT_FDCWD); - assert(path); - - if (fstatat(dir_fd, path, &st, (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0) - return -errno; - - return stat_verify_regular(&st); -} - -int stat_verify_directory(const struct stat *st) { - assert(st); - - if (S_ISLNK(st->st_mode)) - return -ELOOP; - - if (!S_ISDIR(st->st_mode)) - return -ENOTDIR; - - return 0; -} - -int fd_verify_directory(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return stat_verify_directory(&st); -} - int proc_mounted(void) { int r; @@ -363,8 +364,7 @@ bool stat_inode_same(const struct stat *a, const struct stat *b) { /* Returns if the specified stat structure references the same (though possibly modified) inode. Does * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */ - return a && b && - (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ + return stat_is_set(a) && stat_is_set(b) && ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ a->st_dev == b->st_dev && a->st_ino == b->st_ino; @@ -392,9 +392,8 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) { /* Same as stat_inode_same() but for struct statx */ - return a && b && + return statx_is_set(a) && statx_is_set(b) && FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) && - (a->stx_mode & S_IFMT) != 0 && ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 && a->stx_dev_major == b->stx_dev_major && a->stx_dev_minor == b->stx_dev_minor && @@ -402,7 +401,7 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) { } bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) { - if (!a || !b) + if (!new_statx_is_set(a) || !new_statx_is_set(b)) return false; /* if we have the mount ID, that's all we need */ @@ -498,8 +497,8 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) { } void inode_hash_func(const struct stat *q, struct siphash *state) { - siphash24_compress(&q->st_dev, sizeof(q->st_dev), state); - siphash24_compress(&q->st_ino, sizeof(q->st_ino), state); + siphash24_compress_typesafe(q->st_dev, state); + siphash24_compress_typesafe(q->st_ino, state); } int inode_compare_func(const struct stat *a, const struct stat *b) { @@ -536,5 +535,29 @@ const char* inode_type_to_string(mode_t m) { return "sock"; } + /* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will + * return an .st_mode where we'll return NULL here! */ return NULL; } + +mode_t inode_type_from_string(const char *s) { + if (!s) + return MODE_INVALID; + + if (streq(s, "reg")) + return S_IFREG; + if (streq(s, "dir")) + return S_IFDIR; + if (streq(s, "lnk")) + return S_IFLNK; + if (streq(s, "chr")) + return S_IFCHR; + if (streq(s, "blk")) + return S_IFBLK; + if (streq(s, "fifo")) + return S_IFIFO; + if (streq(s, "sock")) + return S_IFSOCK; + + return MODE_INVALID; +} diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 3501406..7556f8f 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -9,18 +9,28 @@ #include <sys/types.h> #include <sys/vfs.h> +#include "fs-util.h" #include "macro.h" #include "missing_stat.h" #include "siphash24.h" +#include "time-util.h" +int stat_verify_regular(const struct stat *st); +int verify_regular_at(int fd, const char *path, bool follow); +int fd_verify_regular(int fd); + +int stat_verify_directory(const struct stat *st); +int fd_verify_directory(int fd); +int is_dir_at(int fd, const char *path, bool follow); +int is_dir(const char *path, bool follow); + +int stat_verify_symlink(const struct stat *st); int is_symlink(const char *path); -int is_dir_full(int atfd, const char *fname, bool follow); -static inline int is_dir(const char *path, bool follow) { - return is_dir_full(AT_FDCWD, path, follow); -} -static inline int is_dir_fd(int fd) { - return is_dir_full(fd, NULL, false); -} + +int stat_verify_linked(const struct stat *st); +int fd_verify_linked(int fd); + +int stat_verify_device_node(const struct stat *st); int is_device_node(const char *path); int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup); @@ -35,13 +45,16 @@ static inline int null_or_empty_path(const char *fn) { return null_or_empty_path_with_root(fn, NULL); } +int fd_is_read_only_fs(int fd); int path_is_read_only_fs(const char *path); int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags); - static inline int inode_same(const char *filea, const char *fileb, int flags) { return inode_same_at(AT_FDCWD, filea, AT_FDCWD, fileb, flags); } +static inline int fd_inode_same(int fda, int fdb) { + return inode_same_at(fda, NULL, fdb, NULL, AT_EMPTY_PATH); +} /* The .f_type field of struct statfs is really weird defined on * different archs. Let's give its type a name. */ @@ -71,16 +84,6 @@ 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); - -int stat_verify_directory(const struct stat *st); -int fd_verify_directory(int fd); - int proc_mounted(void); bool stat_inode_same(const struct stat *a, const struct stat *b); @@ -112,8 +115,31 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret); } var #endif +static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) { + return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec }); +} +static inline nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) { + return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec }); +} + void inode_hash_func(const struct stat *q, struct siphash *state); int inode_compare_func(const struct stat *a, const struct stat *b); extern const struct hash_ops inode_hash_ops; const char* inode_type_to_string(mode_t m); +mode_t inode_type_from_string(const char *s); + +/* Macros that check whether the stat/statx structures have been initialized already. For "struct stat" we + * use a check for .st_dev being non-zero, since the kernel unconditionally fills that in, mapping the file + * to its originating superblock, regardless if the fs is block based or virtual (we also check for .st_mode + * being MODE_INVALID, since we use that as an invalid marker for separate mode_t fields). For "struct statx" + * we use the .stx_mask field, which must be non-zero if any of the fields have already been initialized. */ +static inline bool stat_is_set(const struct stat *st) { + return st && st->st_dev != 0 && st->st_mode != MODE_INVALID; +} +static inline bool statx_is_set(const struct statx *sx) { + return sx && sx->stx_mask != 0; +} +static inline bool new_statx_is_set(const struct new_statx *sx) { + return sx && sx->stx_mask != 0; +} diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h index 4e93ac9..0a2239d 100644 --- a/src/basic/stdio-util.h +++ b/src/basic/stdio-util.h @@ -9,14 +9,12 @@ #include "macro.h" _printf_(3, 4) -static inline char *snprintf_ok(char *buf, size_t len, const char *format, ...) { +static inline char* snprintf_ok(char *buf, size_t len, const char *format, ...) { va_list ap; int r; va_start(ap, format); - DISABLE_WARNING_FORMAT_NONLITERAL; r = vsnprintf(buf, len, format, ap); - REENABLE_WARNING; va_end(ap); return r >= 0 && (size_t) r < len ? buf : NULL; diff --git a/src/basic/string-table.h b/src/basic/string-table.h index 3be70df..d1d90df 100644 --- a/src/basic/string-table.h +++ b/src/basic/string-table.h @@ -47,10 +47,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k s = strdup(name##_table[i]); \ if (!s) \ return -ENOMEM; \ - } else { \ - if (asprintf(&s, "%i", i) < 0) \ - return -ENOMEM; \ - } \ + } else if (asprintf(&s, "%i", i) < 0) \ + return -ENOMEM; \ *str = s; \ return 0; \ } diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 7329bfa..d0d33a4 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -11,6 +11,7 @@ #include "extract-word.h" #include "fd-util.h" #include "fileio.h" +#include "glyph-util.h" #include "gunicode.h" #include "locale-util.h" #include "macro.h" @@ -282,16 +283,9 @@ bool string_has_cc(const char *p, const char *ok) { } static int write_ellipsis(char *buf, bool unicode) { - if (unicode || is_locale_utf8()) { - buf[0] = 0xe2; /* tri-dot ellipsis: … */ - buf[1] = 0x80; - buf[2] = 0xa6; - } else { - buf[0] = '.'; - buf[1] = '.'; - buf[2] = '.'; - } - + const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode); + assert(strlen(s) == 3); + memcpy(buf, s, 3); return 3; } @@ -398,8 +392,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le x = ((new_length - need_space) * percent + 50) / 100; assert(x <= new_length - need_space); - memcpy(t, s, x); - write_ellipsis(t + x, false); + write_ellipsis(mempcpy(t, s, x), /* unicode = */ false); suffix_len = new_length - x - need_space; memcpy(t + x + 3, s + old_length - suffix_len, suffix_len); *(t + x + 3 + suffix_len) = '\0'; @@ -520,13 +513,8 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne if (!e) return NULL; - /* - printf("old_length=%zu new_length=%zu x=%zu len=%zu len2=%zu k=%zu\n", - old_length, new_length, x, len, len2, k); - */ - memcpy_safe(e, s, len); - write_ellipsis(e + len, true); + write_ellipsis(e + len, /* unicode = */ true); char *dst = e + len + 3; @@ -562,7 +550,9 @@ char *cellescape(char *buf, size_t len, const char *s) { size_t i = 0, last_char_width[4] = {}, k = 0; + assert(buf); assert(len > 0); /* at least a terminating NUL */ + assert(s); for (;;) { char four[4]; @@ -603,7 +593,7 @@ char *cellescape(char *buf, size_t len, const char *s) { } if (i + 4 <= len) /* yay, enough space */ - i += write_ellipsis(buf + i, false); + i += write_ellipsis(buf + i, /* unicode = */ false); else if (i + 3 <= len) { /* only space for ".." */ buf[i++] = '.'; buf[i++] = '.'; @@ -612,7 +602,7 @@ char *cellescape(char *buf, size_t len, const char *s) { else assert(i + 1 <= len); - done: +done: buf[i] = '\0'; return buf; } @@ -620,6 +610,9 @@ char *cellescape(char *buf, size_t len, const char *s) { char* strshorten(char *s, size_t l) { assert(s); + if (l >= SIZE_MAX-1) /* Would not change anything */ + return s; + if (strnlen(s, l+1) > l) s[l] = 0; @@ -993,7 +986,7 @@ int strextendf_with_separator(char **x, const char *separator, const char *forma return 0; oom: - /* truncate the bytes added after the first vsnprintf() attempt again */ + /* truncate the bytes added after memcpy_safe() again */ (*x)[m] = 0; return -ENOMEM; } @@ -1123,6 +1116,24 @@ int free_and_strndup(char **p, const char *s, size_t l) { return 1; } +int strdup_to_full(char **ret, const char *src) { + if (!src) { + if (ret) + *ret = NULL; + + return 0; + } else { + if (ret) { + char *t = strdup(src); + if (!t) + return -ENOMEM; + *ret = t; + } + + return 1; + } +}; + bool string_is_safe(const char *p) { if (!p) return false; @@ -1232,54 +1243,31 @@ int string_extract_line(const char *s, size_t i, char **ret) { return -ENOMEM; *ret = m; - return !isempty(q + 1); /* more coming? */ - } else { - if (p == s) - *ret = NULL; /* Just use the input string */ - else { - char *m; - - m = strdup(p); - if (!m) - return -ENOMEM; - - *ret = m; - } - - return 0; /* The end */ - } + return !isempty(q + 1); /* More coming? */ + } else + /* Tell the caller to use the input string if equal */ + return strdup_to(ret, p != s ? p : NULL); } - if (!q) { - char *m; - + if (!q) /* No more lines, return empty line */ - - m = strdup(""); - if (!m) - return -ENOMEM; - - *ret = m; - return 0; /* The end */ - } + return strdup_to(ret, ""); p = q + 1; c++; } } -int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) { - /* In the default mode with no separators specified, we split on whitespace and - * don't coalesce separators. */ +int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) { + /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */ const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0; - const char *found = NULL; + int r; - for (const char *p = string;;) { + for (;;) { _cleanup_free_ char *w = NULL; - int r; - r = extract_first_word(&p, &w, separators, flags); + r = extract_first_word(&string, &w, separators, flags); if (r < 0) return r; if (r == 0) @@ -1420,18 +1408,6 @@ char *find_line_startswith(const char *haystack, const char *needle) { return p + strlen(needle); } -char *startswith_strv(const char *string, char **strv) { - char *found = NULL; - - STRV_FOREACH(i, strv) { - found = startswith(string, *i); - if (found) - break; - } - - return found; -} - bool version_is_valid(const char *s) { if (isempty(s)) return false; @@ -1519,3 +1495,22 @@ ssize_t strlevenshtein(const char *x, const char *y) { return t1[yl]; } + +char *strrstr(const char *haystack, const char *needle) { + /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */ + + if (!haystack || !needle) + return NULL; + + /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the + * last char, not before. */ + if (*needle == 0) + return strchr(haystack, 0); + + for (const char *p = strstr(haystack, needle), *q; p; p = q) { + q = strstr(p + 1, needle); + if (!q) + return (char *) p; + } + return NULL; +} diff --git a/src/basic/string-util.h b/src/basic/string-util.h index b6d8be3..ff5efbc 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -133,7 +133,7 @@ static inline char *truncate_nl(char *s) { return truncate_nl_full(s, NULL); } -static inline char *skip_leading_chars(const char *s, const char *bad) { +static inline char* skip_leading_chars(const char *s, const char *bad) { if (!s) return NULL; @@ -224,6 +224,12 @@ static inline int free_and_strdup_warn(char **p, const char *s) { } int free_and_strndup(char **p, const char *s, size_t l); +int strdup_to_full(char **ret, const char *src); +static inline int strdup_to(char **ret, const char *src) { + int r = strdup_to_full(ASSERT_PTR(ret), src); + return r < 0 ? r : 0; /* Suppress return value of 1. */ +} + bool string_is_safe(const char *p) _pure_; DISABLE_WARNING_STRINGOP_TRUNCATION; @@ -265,7 +271,7 @@ char* string_erase(char *x); int string_truncate_lines(const char *s, size_t n_lines, char **ret); int string_extract_line(const char *s, size_t i, char **ret); -int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word); +int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word); static inline int string_contains_word(const char *string, const char *separators, const char *word) { return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL); } @@ -291,34 +297,10 @@ char *strdupcspn(const char *a, const char *reject); char *find_line_startswith(const char *haystack, const char *needle); -char *startswith_strv(const char *string, char **strv); - -#define STARTSWITH_SET(p, ...) \ - startswith_strv(p, STRV_MAKE(__VA_ARGS__)) - bool version_is_valid(const char *s); bool version_is_valid_versionspec(const char *s); ssize_t strlevenshtein(const char *x, const char *y); -static inline int strdup_or_null(const char *s, char **ret) { - char *c; - - assert(ret); - - /* This is a lot like strdup(), but is happy with NULL strings, and does not treat that as error, but - * copies the NULL value. */ - - if (!s) { - *ret = NULL; - return 0; - } - - c = strdup(s); - if (!c) - return -ENOMEM; - - *ret = c; - return 1; -} +char *strrstr(const char *haystack, const char *needle); diff --git a/src/basic/strv.c b/src/basic/strv.c index 1065e1b..d081821 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -242,21 +242,19 @@ rollback: return -ENOMEM; } -int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { +int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix) { int r; STRV_FOREACH(s, b) { char *v; - v = strjoin(*s, suffix); + v = strjoin(strempty(prefix), *s, suffix); if (!v) return -ENOMEM; - r = strv_push(a, v); - if (r < 0) { - free(v); + r = strv_consume(a, v); + if (r < 0) return r; - } } return 0; @@ -358,7 +356,7 @@ int strv_split_colon_pairs(char ***t, const char *s) { const char *p = tuple; r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, - &first, &second, NULL); + &first, &second); if (r < 0) return r; if (r == 0) @@ -505,29 +503,31 @@ int strv_insert(char ***l, size_t position, char *value) { char **c; size_t n, m; + assert(l); + if (!value) return 0; n = strv_length(*l); position = MIN(position, n); - /* increase and check for overflow */ - m = n + 2; - if (m < n) + /* check for overflow and increase*/ + if (n > SIZE_MAX - 2) return -ENOMEM; + m = n + 2; - c = new(char*, m); + c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*)); if (!c) return -ENOMEM; - for (size_t i = 0; i < position; i++) - c[i] = (*l)[i]; + if (n > position) + memmove(c + position + 1, c + position, (n - position) * sizeof(char*)); + c[position] = value; - for (size_t i = position; i < n; i++) - c[i+1] = (*l)[i]; - c[n+1] = NULL; + c[n + 1] = NULL; - return free_and_replace(*l, c); + *l = c; + return 0; } int strv_consume_with_size(char ***l, size_t *n, char *value) { @@ -588,39 +588,63 @@ int strv_extend_with_size(char ***l, size_t *n, const char *value) { return strv_consume_with_size(l, n, v); } -int strv_extend_front(char ***l, const char *value) { +int strv_extend_many_internal(char ***l, const char *value, ...) { + va_list ap; size_t n, m; - char *v, **c; + int r; assert(l); - /* Like strv_extend(), but prepends rather than appends the new entry */ + m = n = strv_length(*l); - if (!value) - return 0; + r = 0; + va_start(ap, value); + for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) { + if (!s) + continue; - n = strv_length(*l); + if (m > SIZE_MAX-1) { /* overflow */ + r = -ENOMEM; + break; + } + m++; + } + va_end(ap); - /* Increase and overflow check. */ - m = n + 2; - if (m < n) + if (r < 0) + return r; + if (m > SIZE_MAX-1) return -ENOMEM; - v = strdup(value); - if (!v) + char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*)); + if (!c) return -ENOMEM; + *l = c; - c = reallocarray(*l, m, sizeof(char*)); - if (!c) { - free(v); - return -ENOMEM; + r = 0; + size_t i = n; + va_start(ap, value); + for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) { + if (!s) + continue; + + c[i] = strdup(s); + if (!c[i]) { + r = -ENOMEM; + break; + } + i++; } + va_end(ap); - memmove(c+1, c, n * sizeof(char*)); - c[0] = v; - c[n+1] = NULL; + if (r < 0) { + /* rollback on error */ + for (size_t j = n; j < i; j++) + c[j] = mfree(c[j]); + return r; + } - *l = c; + c[i] = NULL; return 0; } @@ -722,6 +746,26 @@ int strv_extendf(char ***l, const char *format, ...) { return strv_consume(l, x); } +char* startswith_strv(const char *s, char * const *l) { + STRV_FOREACH(i, l) { + char *found = startswith(s, *i); + if (found) + return found; + } + + return NULL; +} + +char* endswith_strv(const char *s, char * const *l) { + STRV_FOREACH(i, l) { + char *found = endswith(s, *i); + if (found) + return found; + } + + return NULL; +} + char** strv_reverse(char **l) { size_t n; @@ -848,13 +892,15 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) { bool b = false; int r; + assert(f); + /* Like fputs(), but for strv, and with a less stupid argument order */ if (!space) space = &b; STRV_FOREACH(s, l) { - r = fputs_with_space(f, *s, separator, space); + r = fputs_with_separator(f, *s, separator, space); if (r < 0) return r; } diff --git a/src/basic/strv.h b/src/basic/strv.h index 03089d5..169737d 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -43,7 +43,10 @@ int strv_copy_unless_empty(char * const *l, char ***ret); size_t strv_length(char * const *l) _pure_; int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); -int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix); +int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix); +static inline int strv_extend_strv_concat(char ***a, const char* const *b, const char *suffix) { + return strv_extend_strv_biconcat(a, NULL, b, suffix); +} int strv_prepend(char ***l, const char *value); /* _with_size() are lower-level functions where the size can be provided externally, @@ -55,8 +58,10 @@ static inline int strv_extend(char ***l, const char *value) { return strv_extend_with_size(l, NULL, value); } +int strv_extend_many_internal(char ***l, const char *value, ...); +#define strv_extend_many(l, ...) strv_extend_many_internal(l, __VA_ARGS__, POINTER_MAX) + int strv_extendf(char ***l, const char *format, ...) _printf_(2,3); -int strv_extend_front(char ***l, const char *value); int strv_push_with_size(char ***l, size_t *n, char *value); static inline int strv_push(char ***l, char *value) { @@ -161,6 +166,16 @@ static inline void strv_print(char * const *l) { strv_print_full(l, NULL); } +char* startswith_strv(const char *s, char * const *l); + +#define STARTSWITH_SET(p, ...) \ + startswith_strv(p, STRV_MAKE(__VA_ARGS__)) + +char* endswith_strv(const char *s, char * const *l); + +#define ENDSWITH_SET(p, ...) \ + endswith_strv(p, STRV_MAKE(__VA_ARGS__)) + #define strv_from_stdarg_alloca(first) \ ({ \ char **_l; \ @@ -204,18 +219,6 @@ static inline void strv_print(char * const *l) { _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \ }) -#define ENDSWITH_SET(p, ...) \ - ({ \ - const char *_p = (p); \ - char *_found = NULL; \ - STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ - _found = endswith(_p, *_i); \ - if (_found) \ - break; \ - } \ - _found; \ - }) - #define _FOREACH_STRING(uniq, x, y, ...) \ for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \ x; \ diff --git a/src/basic/syscall-list.txt b/src/basic/syscall-list.txt index 1c335bb..4a7b7fb 100644 --- a/src/basic/syscall-list.txt +++ b/src/basic/syscall-list.txt @@ -188,12 +188,16 @@ lgetxattr link linkat listen +listmount listxattr llistxattr lookup_dcookie lremovexattr lseek lsetxattr +lsm_get_self_attr +lsm_list_modules +lsm_set_self_attr lstat lstat64 madvise @@ -229,6 +233,7 @@ mq_timedsend mq_timedsend_time64 mq_unlink mremap +mseal msgctl msgget msgrcv @@ -449,6 +454,7 @@ stat stat64 statfs statfs64 +statmount statx stime subpage_prot diff --git a/src/basic/syscalls-alpha.txt b/src/basic/syscalls-alpha.txt index d3ed3a4..da50c04 100644 --- a/src/basic/syscalls-alpha.txt +++ b/src/basic/syscalls-alpha.txt @@ -38,7 +38,7 @@ clock_nanosleep_time64 clock_settime 419 clock_settime64 clone 312 -clone3 +clone3 545 close 6 close_range 546 connect 98 @@ -188,12 +188,16 @@ lgetxattr 386 link 9 linkat 458 listen 106 +listmount 568 listxattr 388 llistxattr 389 lookup_dcookie 406 lremovexattr 392 lseek 19 lsetxattr 383 +lsm_get_self_attr 569 +lsm_list_modules 571 +lsm_set_self_attr 570 lstat 68 lstat64 426 madvise 75 @@ -229,6 +233,7 @@ mq_timedsend 434 mq_timedsend_time64 mq_unlink 433 mremap 341 +mseal 572 msgctl 200 msgget 201 msgrcv 202 @@ -449,6 +454,7 @@ stat 67 stat64 425 statfs 328 statfs64 528 +statmount 567 statx 522 stime subpage_prot diff --git a/src/basic/syscalls-arc.txt b/src/basic/syscalls-arc.txt index 951ef56..cdb8a53 100644 --- a/src/basic/syscalls-arc.txt +++ b/src/basic/syscalls-arc.txt @@ -188,12 +188,16 @@ lgetxattr 9 link linkat 37 listen 201 +listmount 458 listxattr 11 llistxattr 12 lookup_dcookie 18 lremovexattr 15 lseek lsetxattr 6 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat lstat64 madvise 233 @@ -229,6 +233,7 @@ mq_timedsend 182 mq_timedsend_time64 418 mq_unlink 181 mremap 216 +mseal 462 msgctl 187 msgget 186 msgrcv 188 @@ -449,6 +454,7 @@ stat stat64 statfs statfs64 43 +statmount 457 statx 291 stime subpage_prot diff --git a/src/basic/syscalls-arm.txt b/src/basic/syscalls-arm.txt index 1c0e66f..743dd87 100644 --- a/src/basic/syscalls-arm.txt +++ b/src/basic/syscalls-arm.txt @@ -188,12 +188,16 @@ lgetxattr 230 link 9 linkat 330 listen 284 +listmount 458 listxattr 232 llistxattr 233 lookup_dcookie 249 lremovexattr 236 lseek 19 lsetxattr 227 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 107 lstat64 196 madvise 220 @@ -229,6 +233,7 @@ mq_timedsend 276 mq_timedsend_time64 418 mq_unlink 275 mremap 163 +mseal 462 msgctl 304 msgget 303 msgrcv 302 @@ -449,6 +454,7 @@ stat 106 stat64 195 statfs 99 statfs64 266 +statmount 457 statx 397 stime subpage_prot diff --git a/src/basic/syscalls-arm64.txt b/src/basic/syscalls-arm64.txt index b8602a1..e2fc548 100644 --- a/src/basic/syscalls-arm64.txt +++ b/src/basic/syscalls-arm64.txt @@ -188,12 +188,16 @@ lgetxattr 9 link linkat 37 listen 201 +listmount 458 listxattr 11 llistxattr 12 lookup_dcookie 18 lremovexattr 15 lseek 62 lsetxattr 6 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat lstat64 madvise 233 @@ -229,6 +233,7 @@ mq_timedsend 182 mq_timedsend_time64 mq_unlink 181 mremap 216 +mseal 462 msgctl 187 msgget 186 msgrcv 188 @@ -449,6 +454,7 @@ stat stat64 statfs 43 statfs64 +statmount 457 statx 291 stime subpage_prot diff --git a/src/basic/syscalls-i386.txt b/src/basic/syscalls-i386.txt index 6d0c57f..3c571e8 100644 --- a/src/basic/syscalls-i386.txt +++ b/src/basic/syscalls-i386.txt @@ -188,12 +188,16 @@ lgetxattr 230 link 9 linkat 303 listen 363 +listmount 458 listxattr 232 llistxattr 233 lookup_dcookie 253 lremovexattr 236 lseek 19 lsetxattr 227 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 107 lstat64 196 madvise 219 @@ -229,6 +233,7 @@ mq_timedsend 279 mq_timedsend_time64 418 mq_unlink 278 mremap 163 +mseal 462 msgctl 402 msgget 399 msgrcv 401 @@ -449,6 +454,7 @@ stat 106 stat64 195 statfs 99 statfs64 268 +statmount 457 statx 383 stime 25 subpage_prot diff --git a/src/basic/syscalls-loongarch64.txt b/src/basic/syscalls-loongarch64.txt index 34a45cb..ba69d80 100644 --- a/src/basic/syscalls-loongarch64.txt +++ b/src/basic/syscalls-loongarch64.txt @@ -188,12 +188,16 @@ lgetxattr 9 link linkat 37 listen 201 +listmount 458 listxattr 11 llistxattr 12 lookup_dcookie 18 lremovexattr 15 lseek 62 lsetxattr 6 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat lstat64 madvise 233 @@ -229,6 +233,7 @@ mq_timedsend 182 mq_timedsend_time64 mq_unlink 181 mremap 216 +mseal 462 msgctl 187 msgget 186 msgrcv 188 @@ -449,6 +454,7 @@ stat stat64 statfs 43 statfs64 +statmount 457 statx 291 stime subpage_prot diff --git a/src/basic/syscalls-m68k.txt b/src/basic/syscalls-m68k.txt index 712f272..032e354 100644 --- a/src/basic/syscalls-m68k.txt +++ b/src/basic/syscalls-m68k.txt @@ -188,12 +188,16 @@ lgetxattr 227 link 9 linkat 296 listen 360 +listmount 458 listxattr 229 llistxattr 230 lookup_dcookie 248 lremovexattr 233 lseek 19 lsetxattr 224 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 107 lstat64 196 madvise 238 @@ -229,6 +233,7 @@ mq_timedsend 273 mq_timedsend_time64 418 mq_unlink 272 mremap 163 +mseal 462 msgctl 402 msgget 399 msgrcv 401 @@ -449,6 +454,7 @@ stat 106 stat64 195 statfs 99 statfs64 263 +statmount 457 statx 379 stime 25 subpage_prot diff --git a/src/basic/syscalls-mips64.txt b/src/basic/syscalls-mips64.txt index 2d0984e..b470f8d 100644 --- a/src/basic/syscalls-mips64.txt +++ b/src/basic/syscalls-mips64.txt @@ -188,12 +188,16 @@ lgetxattr 5184 link 5084 linkat 5255 listen 5049 +listmount 5458 listxattr 5186 llistxattr 5187 lookup_dcookie 5206 lremovexattr 5190 lseek 5008 lsetxattr 5181 +lsm_get_self_attr 5459 +lsm_list_modules 5461 +lsm_set_self_attr 5460 lstat 5006 lstat64 madvise 5027 @@ -229,6 +233,7 @@ mq_timedsend 5232 mq_timedsend_time64 mq_unlink 5231 mremap 5024 +mseal 5462 msgctl 5069 msgget 5066 msgrcv 5068 @@ -449,6 +454,7 @@ stat 5004 stat64 statfs 5134 statfs64 +statmount 5457 statx 5326 stime subpage_prot diff --git a/src/basic/syscalls-mips64n32.txt b/src/basic/syscalls-mips64n32.txt index 4475867..30ddfca 100644 --- a/src/basic/syscalls-mips64n32.txt +++ b/src/basic/syscalls-mips64n32.txt @@ -188,12 +188,16 @@ lgetxattr 6184 link 6084 linkat 6259 listen 6049 +listmount 6458 listxattr 6186 llistxattr 6187 lookup_dcookie 6206 lremovexattr 6190 lseek 6008 lsetxattr 6181 +lsm_get_self_attr 6459 +lsm_list_modules 6461 +lsm_set_self_attr 6460 lstat 6006 lstat64 madvise 6027 @@ -229,6 +233,7 @@ mq_timedsend 6236 mq_timedsend_time64 6418 mq_unlink 6235 mremap 6024 +mseal 6462 msgctl 6069 msgget 6066 msgrcv 6068 @@ -449,6 +454,7 @@ stat 6004 stat64 statfs 6134 statfs64 6217 +statmount 6457 statx 6330 stime subpage_prot diff --git a/src/basic/syscalls-mipso32.txt b/src/basic/syscalls-mipso32.txt index 0254cb3..26cb3c1 100644 --- a/src/basic/syscalls-mipso32.txt +++ b/src/basic/syscalls-mipso32.txt @@ -188,12 +188,16 @@ lgetxattr 4228 link 4009 linkat 4296 listen 4174 +listmount 4458 listxattr 4230 llistxattr 4231 lookup_dcookie 4247 lremovexattr 4234 lseek 4019 lsetxattr 4225 +lsm_get_self_attr 4459 +lsm_list_modules 4461 +lsm_set_self_attr 4460 lstat 4107 lstat64 4214 madvise 4218 @@ -229,6 +233,7 @@ mq_timedsend 4273 mq_timedsend_time64 4418 mq_unlink 4272 mremap 4167 +mseal 4462 msgctl 4402 msgget 4399 msgrcv 4401 @@ -449,6 +454,7 @@ stat 4106 stat64 4213 statfs 4099 statfs64 4255 +statmount 4457 statx 4366 stime 4025 subpage_prot diff --git a/src/basic/syscalls-parisc.txt b/src/basic/syscalls-parisc.txt index 2bb1de5..99c7712 100644 --- a/src/basic/syscalls-parisc.txt +++ b/src/basic/syscalls-parisc.txt @@ -188,12 +188,16 @@ lgetxattr 242 link 9 linkat 283 listen 32 +listmount 458 listxattr 244 llistxattr 245 lookup_dcookie 223 lremovexattr 248 lseek 19 lsetxattr 239 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 84 lstat64 198 madvise 119 @@ -229,6 +233,7 @@ mq_timedsend 231 mq_timedsend_time64 418 mq_unlink 230 mremap 163 +mseal 462 msgctl 191 msgget 190 msgrcv 189 @@ -449,6 +454,7 @@ stat 18 stat64 101 statfs 99 statfs64 298 +statmount 457 statx 349 stime 25 subpage_prot diff --git a/src/basic/syscalls-powerpc.txt b/src/basic/syscalls-powerpc.txt index a8c1b1b..a1c6452 100644 --- a/src/basic/syscalls-powerpc.txt +++ b/src/basic/syscalls-powerpc.txt @@ -188,12 +188,16 @@ lgetxattr 213 link 9 linkat 294 listen 329 +listmount 458 listxattr 215 llistxattr 216 lookup_dcookie 235 lremovexattr 219 lseek 19 lsetxattr 210 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 107 lstat64 196 madvise 205 @@ -229,6 +233,7 @@ mq_timedsend 264 mq_timedsend_time64 418 mq_unlink 263 mremap 163 +mseal 462 msgctl 402 msgget 399 msgrcv 401 @@ -449,6 +454,7 @@ stat 106 stat64 195 statfs 99 statfs64 252 +statmount 457 statx 383 stime 25 subpage_prot 310 diff --git a/src/basic/syscalls-powerpc64.txt b/src/basic/syscalls-powerpc64.txt index 824cc61..992c61d 100644 --- a/src/basic/syscalls-powerpc64.txt +++ b/src/basic/syscalls-powerpc64.txt @@ -188,12 +188,16 @@ lgetxattr 213 link 9 linkat 294 listen 329 +listmount 458 listxattr 215 llistxattr 216 lookup_dcookie 235 lremovexattr 219 lseek 19 lsetxattr 210 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 107 lstat64 madvise 205 @@ -229,6 +233,7 @@ mq_timedsend 264 mq_timedsend_time64 mq_unlink 263 mremap 163 +mseal 462 msgctl 402 msgget 399 msgrcv 401 @@ -449,6 +454,7 @@ stat 106 stat64 statfs 99 statfs64 252 +statmount 457 statx 383 stime 25 subpage_prot 310 diff --git a/src/basic/syscalls-riscv32.txt b/src/basic/syscalls-riscv32.txt index 5011956..3af7cdb 100644 --- a/src/basic/syscalls-riscv32.txt +++ b/src/basic/syscalls-riscv32.txt @@ -188,12 +188,16 @@ lgetxattr 9 link linkat 37 listen 201 +listmount 458 listxattr 11 llistxattr 12 lookup_dcookie 18 lremovexattr 15 lseek lsetxattr 6 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat lstat64 madvise 233 @@ -229,6 +233,7 @@ mq_timedsend mq_timedsend_time64 418 mq_unlink 181 mremap 216 +mseal 462 msgctl 187 msgget 186 msgrcv 188 @@ -449,6 +454,7 @@ stat stat64 statfs statfs64 43 +statmount 457 statx 291 stime subpage_prot diff --git a/src/basic/syscalls-riscv64.txt b/src/basic/syscalls-riscv64.txt index ba00b90..7a1882a 100644 --- a/src/basic/syscalls-riscv64.txt +++ b/src/basic/syscalls-riscv64.txt @@ -188,12 +188,16 @@ lgetxattr 9 link linkat 37 listen 201 +listmount 458 listxattr 11 llistxattr 12 lookup_dcookie 18 lremovexattr 15 lseek 62 lsetxattr 6 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat lstat64 madvise 233 @@ -229,6 +233,7 @@ mq_timedsend 182 mq_timedsend_time64 mq_unlink 181 mremap 216 +mseal 462 msgctl 187 msgget 186 msgrcv 188 @@ -449,6 +454,7 @@ stat stat64 statfs 43 statfs64 +statmount 457 statx 291 stime subpage_prot diff --git a/src/basic/syscalls-s390.txt b/src/basic/syscalls-s390.txt index c81f795..855d5c4 100644 --- a/src/basic/syscalls-s390.txt +++ b/src/basic/syscalls-s390.txt @@ -188,12 +188,16 @@ lgetxattr 228 link 9 linkat 296 listen 363 +listmount 458 listxattr 230 llistxattr 231 lookup_dcookie 110 lremovexattr 234 lseek 19 lsetxattr 225 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 107 lstat64 196 madvise 219 @@ -229,6 +233,7 @@ mq_timedsend 273 mq_timedsend_time64 418 mq_unlink 272 mremap 163 +mseal 462 msgctl 402 msgget 399 msgrcv 401 @@ -449,6 +454,7 @@ stat 106 stat64 195 statfs 99 statfs64 265 +statmount 457 statx 379 stime 25 subpage_prot diff --git a/src/basic/syscalls-s390x.txt b/src/basic/syscalls-s390x.txt index c999fd6..05dcbe9 100644 --- a/src/basic/syscalls-s390x.txt +++ b/src/basic/syscalls-s390x.txt @@ -188,12 +188,16 @@ lgetxattr 228 link 9 linkat 296 listen 363 +listmount 458 listxattr 230 llistxattr 231 lookup_dcookie 110 lremovexattr 234 lseek 19 lsetxattr 225 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 107 lstat64 madvise 219 @@ -229,6 +233,7 @@ mq_timedsend 273 mq_timedsend_time64 mq_unlink 272 mremap 163 +mseal 462 msgctl 402 msgget 399 msgrcv 401 @@ -449,6 +454,7 @@ stat 106 stat64 statfs 99 statfs64 265 +statmount 457 statx 379 stime subpage_prot diff --git a/src/basic/syscalls-sparc.txt b/src/basic/syscalls-sparc.txt index e631d30..f4ff31e 100644 --- a/src/basic/syscalls-sparc.txt +++ b/src/basic/syscalls-sparc.txt @@ -188,12 +188,16 @@ lgetxattr 173 link 9 linkat 292 listen 354 +listmount 458 listxattr 178 llistxattr 179 lookup_dcookie 208 lremovexattr 182 lseek 19 lsetxattr 170 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 40 lstat64 132 madvise 75 @@ -229,6 +233,7 @@ mq_timedsend 275 mq_timedsend_time64 418 mq_unlink 274 mremap 250 +mseal 462 msgctl 402 msgget 399 msgrcv 401 @@ -449,6 +454,7 @@ stat 38 stat64 139 statfs 157 statfs64 234 +statmount 457 statx 360 stime 233 subpage_prot diff --git a/src/basic/syscalls-x86_64.txt b/src/basic/syscalls-x86_64.txt index 52d6176..f314ed0 100644 --- a/src/basic/syscalls-x86_64.txt +++ b/src/basic/syscalls-x86_64.txt @@ -188,12 +188,16 @@ lgetxattr 192 link 86 linkat 265 listen 50 +listmount 458 listxattr 194 llistxattr 195 lookup_dcookie 212 lremovexattr 198 lseek 8 lsetxattr 189 +lsm_get_self_attr 459 +lsm_list_modules 461 +lsm_set_self_attr 460 lstat 6 lstat64 madvise 28 @@ -229,6 +233,7 @@ mq_timedsend 242 mq_timedsend_time64 mq_unlink 241 mremap 25 +mseal 462 msgctl 71 msgget 68 msgrcv 70 @@ -449,6 +454,7 @@ stat 4 stat64 statfs 137 statfs64 +statmount 457 statx 332 stime subpage_prot diff --git a/src/basic/sysctl-util.c b/src/basic/sysctl-util.c index b66a662..9a1933f 100644 --- a/src/basic/sysctl-util.c +++ b/src/basic/sysctl-util.c @@ -96,6 +96,26 @@ int sysctl_write_ip_property(int af, const char *ifname, const char *property, c return sysctl_write(p, value); } +int sysctl_write_ip_neighbor_property(int af, const char *ifname, const char *property, const char *value) { + const char *p; + + assert(property); + assert(value); + assert(ifname); + + if (!IN_SET(af, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + if (ifname) { + if (!ifname_valid_full(ifname, IFNAME_VALID_SPECIAL)) + return -EINVAL; + p = strjoina("net/", af_to_ipv4_ipv6(af), "/neigh/", ifname, "/", property); + } else + p = strjoina("net/", af_to_ipv4_ipv6(af), "/neigh/default/", property); + + return sysctl_write(p, value); +} + int sysctl_read(const char *property, char **ret) { char *p; int r; diff --git a/src/basic/sysctl-util.h b/src/basic/sysctl-util.h index 3236419..7192e8c 100644 --- a/src/basic/sysctl-util.h +++ b/src/basic/sysctl-util.h @@ -19,6 +19,13 @@ static inline int sysctl_write_ip_property_boolean(int af, const char *ifname, c return sysctl_write_ip_property(af, ifname, property, one_zero(value)); } +int sysctl_write_ip_neighbor_property(int af, const char *ifname, const char *property, const char *value); +static inline int sysctl_write_ip_neighbor_property_uint32(int af, const char *ifname, const char *property, uint32_t value) { + char buf[DECIMAL_STR_MAX(uint32_t)]; + xsprintf(buf, "%u", value); + return sysctl_write_ip_neighbor_property(af, ifname, property, buf); +} + #define DEFINE_SYSCTL_WRITE_IP_PROPERTY(name, type, format) \ static inline int sysctl_write_ip_property_##name(int af, const char *ifname, const char *property, type value) { \ char buf[DECIMAL_STR_MAX(type)]; \ diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 530ef9a..dda5920 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -27,6 +27,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hexdecoct.h" #include "inotify-util.h" #include "io-util.h" #include "log.h" @@ -53,6 +54,18 @@ static volatile int cached_on_dev_null = -1; static volatile int cached_color_mode = _COLOR_INVALID; static volatile int cached_underline_enabled = -1; +bool isatty_safe(int fd) { + assert(fd >= 0); + + if (isatty(fd)) + return true; + + /* Be resilient if we're working on stdio, since they're set up by parent process. */ + assert(errno != EBADF || IN_SET(fd, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO)); + + return false; +} + int chvt(int vt) { _cleanup_close_ int fd = -EBADF; @@ -239,7 +252,7 @@ int reset_terminal_fd(int fd, bool switch_to_text) { assert(fd >= 0); - if (isatty(fd) < 1) + if (!isatty_safe(fd)) return log_debug_errno(errno, "Asked to reset a terminal that actually isn't a terminal: %m"); /* We leave locked terminal attributes untouched, so that Plymouth may set whatever it wants to set, @@ -293,6 +306,8 @@ int reset_terminal_fd(int fd, bool switch_to_text) { termios.c_cc[VMIN] = 1; r = RET_NERRNO(tcsetattr(fd, TCSANOW, &termios)); + if (r < 0) + log_debug_errno(r, "Failed to set terminal parameters: %m"); finish: /* Just in case, flush all crap out */ @@ -346,7 +361,7 @@ int open_terminal(const char *name, int mode) { c++; } - if (isatty(fd) < 1) + if (!isatty_safe(fd)) return negative_errno(); return TAKE_FD(fd); @@ -684,18 +699,10 @@ int vtnr_from_tty(const char *tty) { tty = active; } - if (tty == active) - *ret = TAKE_PTR(active); - else { - char *tmp; - - tmp = strdup(tty); - if (!tmp) - return -ENOMEM; - - *ret = tmp; - } + if (tty != active) + return strdup_to(ret, tty); + *ret = TAKE_PTR(active); return 0; } @@ -975,7 +982,7 @@ bool on_tty(void) { } int getttyname_malloc(int fd, char **ret) { - char path[PATH_MAX], *c; /* PATH_MAX is counted *with* the trailing NUL byte */ + char path[PATH_MAX]; /* PATH_MAX is counted *with* the trailing NUL byte */ int r; assert(fd >= 0); @@ -988,12 +995,7 @@ int getttyname_malloc(int fd, char **ret) { if (r > 0) return -r; - c = strdup(skip_dev_prefix(path)); - if (!c) - return -ENOMEM; - - *ret = c; - return 0; + return strdup_to(ret, skip_dev_prefix(path)); } int getttyname_harder(int fd, char **ret) { @@ -1098,13 +1100,9 @@ int get_ctty(pid_t pid, dev_t *ret_devnr, char **ret) { return -EINVAL; if (ret) { - _cleanup_free_ char *b = NULL; - - b = strdup(w); - if (!b) - return -ENOMEM; - - *ret = TAKE_PTR(b); + r = strdup_to(ret, w); + if (r < 0) + return r; } if (ret_devnr) @@ -1198,7 +1196,7 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) { assert(pid > 0); - r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); + r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd); if (r < 0) return r; @@ -1249,7 +1247,7 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { pid_t child; int r; - r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); + r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd); if (r < 0) return r; @@ -1446,38 +1444,33 @@ int vt_reset_keyboard(int fd) { } int vt_restore(int fd) { + static const struct vt_mode mode = { .mode = VT_AUTO, }; - int r, q = 0; - if (isatty(fd) < 1) + int r, ret = 0; + + assert(fd >= 0); + + if (!isatty_safe(fd)) return log_debug_errno(errno, "Asked to restore the VT for an fd that does not refer to a terminal: %m"); if (ioctl(fd, KDSETMODE, KD_TEXT) < 0) - q = log_debug_errno(errno, "Failed to set VT in text mode, ignoring: %m"); + RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT to text mode, ignoring: %m")); r = vt_reset_keyboard(fd); - if (r < 0) { - log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m"); - if (q >= 0) - q = r; - } + if (r < 0) + RET_GATHER(ret, log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m")); - if (ioctl(fd, VT_SETMODE, &mode) < 0) { - log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m"); - if (q >= 0) - q = -errno; - } + if (ioctl(fd, VT_SETMODE, &mode) < 0) + RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m")); r = fchmod_and_chown(fd, TTY_MODE, 0, GID_INVALID); - if (r < 0) { - log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m"); - if (q >= 0) - q = r; - } + if (r < 0) + RET_GATHER(ret, log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m")); - return q; + return ret; } int vt_release(int fd, bool restore) { @@ -1487,7 +1480,7 @@ int vt_release(int fd, bool restore) { * sent by the kernel and optionally reset the VT in text and auto * VT-switching modes. */ - if (isatty(fd) < 1) + if (!isatty_safe(fd)) return log_debug_errno(errno, "Asked to release the VT for an fd that does not refer to a terminal: %m"); if (ioctl(fd, VT_RELDISP, 1) < 0) @@ -1551,3 +1544,264 @@ int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column) return 0; } + +int terminal_reset_ansi_seq(int fd) { + int r, k; + + assert(fd >= 0); + + if (getenv_terminal_is_dumb()) + return 0; + + r = fd_nonblock(fd, true); + if (r < 0) + return log_debug_errno(r, "Failed to set terminal to non-blocking mode: %m"); + + k = loop_write_full(fd, + "\033c" /* reset to initial state */ + "\033[!p" /* soft terminal reset */ + "\033]104\007" /* reset colors */ + "\033[?7h", /* enable line-wrapping */ + SIZE_MAX, + 50 * USEC_PER_MSEC); + if (k < 0) + log_debug_errno(k, "Failed to write to terminal: %m"); + + if (r > 0) { + r = fd_nonblock(fd, false); + if (r < 0) + log_debug_errno(r, "Failed to set terminal back to blocking mode: %m"); + } + + return k < 0 ? k : r; +} + +void termios_disable_echo(struct termios *termios) { + assert(termios); + + termios->c_lflag &= ~(ICANON|ECHO); + termios->c_cc[VMIN] = 1; + termios->c_cc[VTIME] = 0; +} + +typedef enum BackgroundColorState { + BACKGROUND_TEXT, + BACKGROUND_ESCAPE, + BACKGROUND_BRACKET, + BACKGROUND_FIRST_ONE, + BACKGROUND_SECOND_ONE, + BACKGROUND_SEMICOLON, + BACKGROUND_R, + BACKGROUND_G, + BACKGROUND_B, + BACKGROUND_RED, + BACKGROUND_GREEN, + BACKGROUND_BLUE, + BACKGROUND_STRING_TERMINATOR, +} BackgroundColorState; + +typedef struct BackgroundColorContext { + BackgroundColorState state; + uint32_t red, green, blue; + unsigned red_bits, green_bits, blue_bits; +} BackgroundColorContext; + +static int scan_background_color_response( + BackgroundColorContext *context, + const char *buf, + size_t size) { + + assert(context); + assert(buf || size == 0); + + for (size_t i = 0; i < size; i++) { + char c = buf[i]; + + switch (context->state) { + + case BACKGROUND_TEXT: + context->state = c == '\x1B' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT; + break; + + case BACKGROUND_ESCAPE: + context->state = c == ']' ? BACKGROUND_BRACKET : BACKGROUND_TEXT; + break; + + case BACKGROUND_BRACKET: + context->state = c == '1' ? BACKGROUND_FIRST_ONE : BACKGROUND_TEXT; + break; + + case BACKGROUND_FIRST_ONE: + context->state = c == '1' ? BACKGROUND_SECOND_ONE : BACKGROUND_TEXT; + break; + + case BACKGROUND_SECOND_ONE: + context->state = c == ';' ? BACKGROUND_SEMICOLON : BACKGROUND_TEXT; + break; + + case BACKGROUND_SEMICOLON: + context->state = c == 'r' ? BACKGROUND_R : BACKGROUND_TEXT; + break; + + case BACKGROUND_R: + context->state = c == 'g' ? BACKGROUND_G : BACKGROUND_TEXT; + break; + + case BACKGROUND_G: + context->state = c == 'b' ? BACKGROUND_B : BACKGROUND_TEXT; + break; + + case BACKGROUND_B: + context->state = c == ':' ? BACKGROUND_RED : BACKGROUND_TEXT; + break; + + case BACKGROUND_RED: + if (c == '/') + context->state = context->red_bits > 0 ? BACKGROUND_GREEN : BACKGROUND_TEXT; + else { + int d = unhexchar(c); + if (d < 0 || context->red_bits >= sizeof(context->red)*8) + context->state = BACKGROUND_TEXT; + else { + context->red = (context->red << 4) | d; + context->red_bits += 4; + } + } + break; + + case BACKGROUND_GREEN: + if (c == '/') + context->state = context->green_bits > 0 ? BACKGROUND_BLUE : BACKGROUND_TEXT; + else { + int d = unhexchar(c); + if (d < 0 || context->green_bits >= sizeof(context->green)*8) + context->state = BACKGROUND_TEXT; + else { + context->green = (context->green << 4) | d; + context->green_bits += 4; + } + } + break; + + case BACKGROUND_BLUE: + if (c == '\x07') { + if (context->blue_bits > 0) + return 1; /* success! */ + + context->state = BACKGROUND_TEXT; + } else if (c == '\x1b') + context->state = context->blue_bits > 0 ? BACKGROUND_STRING_TERMINATOR : BACKGROUND_TEXT; + else { + int d = unhexchar(c); + if (d < 0 || context->blue_bits >= sizeof(context->blue)*8) + context->state = BACKGROUND_TEXT; + else { + context->blue = (context->blue << 4) | d; + context->blue_bits += 4; + } + } + break; + + case BACKGROUND_STRING_TERMINATOR: + if (c == '\\') + return 1; /* success! */ + + context->state = c == ']' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT; + break; + + } + + /* Reset any colors we might have picked up */ + if (IN_SET(context->state, BACKGROUND_TEXT, BACKGROUND_ESCAPE)) { + /* reset color */ + context->red = context->green = context->blue = 0; + context->red_bits = context->green_bits = context->blue_bits = 0; + } + } + + return 0; /* all good, but not enough data yet */ +} + +int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) { + int r; + + assert(ret_red); + assert(ret_green); + assert(ret_blue); + + if (!colors_enabled()) + return -EOPNOTSUPP; + + if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) + return -EOPNOTSUPP; + + if (streq_ptr(getenv("TERM"), "linux")) { + /* Linux console is black */ + *ret_red = *ret_green = *ret_blue = 0.0; + return 0; + } + + struct termios old_termios; + if (tcgetattr(STDIN_FILENO, &old_termios) < 0) + return -errno; + + struct termios new_termios = old_termios; + termios_disable_echo(&new_termios); + + if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_termios) < 0) + return -errno; + + r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX); + if (r < 0) + goto finish; + + usec_t end = usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC); + char buf[256]; + size_t buf_full = 0; + BackgroundColorContext context = {}; + + for (;;) { + usec_t n = now(CLOCK_MONOTONIC); + + if (n >= end) { + r = -EOPNOTSUPP; + goto finish; + } + + r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n)); + if (r < 0) + goto finish; + if (r == 0) { + r = -EOPNOTSUPP; + goto finish; + } + + ssize_t l; + l = read(STDIN_FILENO, buf, sizeof(buf) - buf_full); + if (l < 0) { + r = -errno; + goto finish; + } + + buf_full += l; + assert(buf_full <= sizeof(buf)); + + r = scan_background_color_response(&context, buf, buf_full); + if (r < 0) + goto finish; + if (r > 0) { + assert(context.red_bits > 0); + *ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1); + assert(context.green_bits > 0); + *ret_green = (double) context.green / ((UINT64_C(1) << context.green_bits) - 1); + assert(context.blue_bits > 0); + *ret_blue = (double) context.blue / ((UINT64_C(1) << context.blue_bits) - 1); + r = 0; + goto finish; + } + } + +finish: + (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_termios); + return r; +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index b1d7aee..ecfe574 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -6,6 +6,7 @@ #include <stdio.h> #include <syslog.h> #include <sys/types.h> +#include <termios.h> #include "macro.h" #include "time-util.h" @@ -59,6 +60,8 @@ /* Other ANSI codes */ #define ANSI_UNDERLINE "\x1B[0;4m" +#define ANSI_ADD_UNDERLINE "\x1B[4m" +#define ANSI_ADD_UNDERLINE_GREY ANSI_ADD_UNDERLINE "\x1B[58;5;245m" #define ANSI_HIGHLIGHT "\x1B[0;1;39m" #define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m" @@ -77,15 +80,25 @@ /* Erase characters until the end of the line */ #define ANSI_ERASE_TO_END_OF_LINE "\x1B[K" +/* Erase characters until end of screen */ +#define ANSI_ERASE_TO_END_OF_SCREEN "\x1B[J" + /* Move cursor up one line */ #define ANSI_REVERSE_LINEFEED "\x1BM" /* Set cursor to top left corner and clear screen */ #define ANSI_HOME_CLEAR "\x1B[H\x1B[2J" +/* Push/pop a window title off the stack of window titles */ +#define ANSI_WINDOW_TITLE_PUSH "\x1b[22;2t" +#define ANSI_WINDOW_TITLE_POP "\x1b[23;2t" + +bool isatty_safe(int fd); + int reset_terminal_fd(int fd, bool switch_to_text); int reset_terminal(const char *name); int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column); +int terminal_reset_ansi_seq(int fd); int open_terminal(const char *name, int mode); @@ -167,7 +180,6 @@ bool underline_enabled(void); bool dev_console_colors_enabled(void); static inline bool colors_enabled(void) { - /* Returns true if colors are considered supported on our stdout. */ return get_color_mode() != COLOR_OFF; } @@ -190,6 +202,15 @@ static inline const char *ansi_underline(void) { return underline_enabled() ? ANSI_UNDERLINE : ""; } +static inline const char *ansi_add_underline(void) { + return underline_enabled() ? ANSI_ADD_UNDERLINE : ""; +} + +static inline const char *ansi_add_underline_grey(void) { + return underline_enabled() ? + (colors_enabled() ? ANSI_ADD_UNDERLINE_GREY : ANSI_ADD_UNDERLINE) : ""; +} + #define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \ static inline const char *ansi_##name(void) { \ return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \ @@ -276,3 +297,7 @@ static inline const char* ansi_highlight_green_red(bool b) { /* This assumes there is a 'tty' group */ #define TTY_MODE 0620 + +void termios_disable_echo(struct termios *termios); + +int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index f9014dc..b94f37c 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -83,7 +83,7 @@ triple_timestamp* triple_timestamp_now(triple_timestamp *ts) { return ts; } -static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) { +usec_t map_clock_usec_raw(usec_t from, usec_t from_base, usec_t to_base) { /* Maps the time 'from' between two clocks, based on a common reference point where the first clock * is at 'from_base' and the second clock at 'to_base'. Basically calculates: @@ -121,7 +121,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) { if (from == USEC_INFINITY) return from; - return map_clock_usec_internal(from, now(from_clock), now(to_clock)); + return map_clock_usec_raw(from, now(from_clock), now(to_clock)); } dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { @@ -150,8 +150,8 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) nowr = now(CLOCK_REALTIME); ts->realtime = u; - ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC)); - ts->boottime = map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)); + ts->monotonic = map_clock_usec_raw(u, nowr, now(CLOCK_MONOTONIC)); + ts->boottime = map_clock_usec_raw(u, nowr, now(CLOCK_BOOTTIME)); return ts; } @@ -169,8 +169,8 @@ triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u) nowb = now(CLOCK_BOOTTIME); ts->boottime = u; - ts->monotonic = map_clock_usec_internal(u, nowb, now(CLOCK_MONOTONIC)); - ts->realtime = map_clock_usec_internal(u, nowb, now(CLOCK_REALTIME)); + ts->monotonic = map_clock_usec_raw(u, nowb, now(CLOCK_MONOTONIC)); + ts->realtime = map_clock_usec_raw(u, nowb, now(CLOCK_REALTIME)); return ts; } @@ -199,8 +199,8 @@ dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) { } nowm = now(CLOCK_BOOTTIME); - ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); - ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME)); + ts->monotonic = map_clock_usec_raw(u, nowm, now(CLOCK_MONOTONIC)); + ts->realtime = map_clock_usec_raw(u, nowm, now(CLOCK_REALTIME)); return ts; } @@ -1429,7 +1429,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) { /* Line format is: * 'country codes' 'coordinates' 'timezone' 'comments' */ - r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL); + r = extract_many_words(&p, NULL, 0, &cc, &co, &tz); if (r < 0) continue; @@ -1474,7 +1474,7 @@ static int get_timezones_from_tzdata_zi(char ***ret) { * Link line format is: * 'Link' 'target' 'alias' * See 'man zic' for more detail. */ - r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL); + r = extract_many_words(&p, NULL, 0, &type, &f1, &f2); if (r < 0) continue; @@ -1517,7 +1517,7 @@ int get_timezones(char ***ret) { /* Always include UTC */ r = strv_extend(&zones, "UTC"); if (r < 0) - return -ENOMEM; + return r; strv_sort(zones); strv_uniq(zones); @@ -1573,7 +1573,7 @@ int verify_timezone(const char *name, int log_level) { r = fd_verify_regular(fd); if (r < 0) - return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); + return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); r = loop_read_exact(fd, buf, 4, false); if (r < 0) @@ -1606,38 +1606,24 @@ bool clock_supported(clockid_t clock) { int get_timezone(char **ret) { _cleanup_free_ char *t = NULL; - const char *e; - char *z; int r; assert(ret); r = readlink_malloc("/etc/localtime", &t); - if (r == -ENOENT) { + if (r == -ENOENT) /* If the symlink does not exist, assume "UTC", like glibc does */ - z = strdup("UTC"); - if (!z) - return -ENOMEM; - - *ret = z; - return 0; - } + return strdup_to(ret, "UTC"); if (r < 0) - return r; /* returns EINVAL if not a symlink */ + return r; /* Return EINVAL if not a symlink */ - e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/"); + const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/"); if (!e) return -EINVAL; - if (!timezone_is_valid(e, LOG_DEBUG)) return -EINVAL; - z = strdup(e); - if (!z) - return -ENOMEM; - - *ret = z; - return 0; + return strdup_to(ret, e); } time_t mktime_or_timegm(struct tm *tm, bool utc) { diff --git a/src/basic/time-util.h b/src/basic/time-util.h index ed4c1aa..f273770 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -71,12 +71,16 @@ typedef enum TimestampStyle { #define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) -#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) -#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) +#define DUAL_TIMESTAMP_NULL ((dual_timestamp) {}) +#define DUAL_TIMESTAMP_INFINITY ((dual_timestamp) { USEC_INFINITY, USEC_INFINITY }) +#define TRIPLE_TIMESTAMP_NULL ((triple_timestamp) {}) + +#define TIMESPEC_OMIT ((const struct timespec) { .tv_nsec = UTIME_OMIT }) usec_t now(clockid_t clock); nsec_t now_nsec(clockid_t clock); +usec_t map_clock_usec_raw(usec_t from, usec_t from_base, usec_t to_base); usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock); dual_timestamp* dual_timestamp_now(dual_timestamp *ts); @@ -219,6 +223,9 @@ static inline int usleep_safe(usec_t usec) { * ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not * CLOCK_MONOTONIC! */ + if (usec == 0) + return 0; + // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h. return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0; } diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c index e77ca94..3a3f7dc 100644 --- a/src/basic/tmpfile-util.c +++ b/src/basic/tmpfile-util.c @@ -330,28 +330,7 @@ int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE return 0; } -static int link_fd(int fd, int newdirfd, const char *newpath) { - int r; - - assert(fd >= 0); - assert(newdirfd >= 0 || newdirfd == AT_FDCWD); - assert(newpath); - - /* Try symlinking via /proc/fd/ first. */ - r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW)); - if (r != -ENOENT) - return r; - - /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a - * more recent kernel, but does not require /proc/ mounted) */ - if (proc_mounted() != 0) - return r; - - return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH)); -} - int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) { - _cleanup_free_ char *tmp = NULL; int r; assert(fd >= 0); @@ -370,33 +349,14 @@ int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, Li r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target)); else r = rename_noreplace(dir_fd, path, dir_fd, target); - if (r < 0) - return r; } else { - - r = link_fd(fd, dir_fd, target); - if (r != -EEXIST || !FLAGS_SET(flags, LINK_TMPFILE_REPLACE)) - return r; - - /* So the target already exists and we were asked to replace it. That sucks a bit, since the kernel's - * linkat() logic does not allow that. We work-around this by linking the file to a random name - * first, and then renaming that to the final name. This reintroduces the race O_TMPFILE kinda is - * trying to fix, but at least the vulnerability window (i.e. where the file is linked into the file - * system under a temporary name) is very short. */ - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; - - if (link_fd(fd, dir_fd, tmp) < 0) - return -EEXIST; /* propagate original error */ - - r = RET_NERRNO(renameat(dir_fd, tmp, dir_fd, target)); - if (r < 0) { - (void) unlinkat(dir_fd, tmp, 0); - return r; - } + if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE)) + r = linkat_replace(fd, /* oldpath= */ NULL, dir_fd, target); + else + r = link_fd(fd, dir_fd, target); } + if (r < 0) + return r; if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) { r = fsync_full(fd); diff --git a/src/basic/uid-alloc-range.c b/src/basic/uid-classification.c index 669cb6d..e2d2ceb 100644 --- a/src/basic/uid-alloc-range.c +++ b/src/basic/uid-classification.c @@ -5,7 +5,7 @@ #include "fileio.h" #include "missing_threads.h" #include "string-util.h" -#include "uid-alloc-range.h" +#include "uid-classification.h" #include "user-util.h" static const UGIDAllocationRange default_ugid_allocation_range = { diff --git a/src/basic/uid-alloc-range.h b/src/basic/uid-classification.h index 5badde1..5badde1 100644 --- a/src/basic/uid-alloc-range.h +++ b/src/basic/uid-classification.h diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c index 8463599..a765881 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -10,12 +10,13 @@ #include "format-util.h" #include "macro.h" #include "path-util.h" +#include "process-util.h" #include "sort-util.h" #include "stat-util.h" #include "uid-range.h" #include "user-util.h" -UidRange *uid_range_free(UidRange *range) { +UIDRange *uid_range_free(UIDRange *range) { if (!range) return NULL; @@ -23,14 +24,14 @@ UidRange *uid_range_free(UidRange *range) { return mfree(range); } -static bool uid_range_entry_intersect(const UidRangeEntry *a, const UidRangeEntry *b) { +static bool uid_range_entry_intersect(const UIDRangeEntry *a, const UIDRangeEntry *b) { assert(a); assert(b); return a->start <= b->start + b->nr && a->start + a->nr >= b->start; } -static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *b) { +static int uid_range_entry_compare(const UIDRangeEntry *a, const UIDRangeEntry *b) { int r; assert(a); @@ -43,7 +44,7 @@ static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry * return CMP(a->nr, b->nr); } -static void uid_range_coalesce(UidRange *range) { +static void uid_range_coalesce(UIDRange *range) { assert(range); if (range->n_entries <= 0) @@ -52,10 +53,10 @@ static void uid_range_coalesce(UidRange *range) { typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare); for (size_t i = 0; i < range->n_entries; i++) { - UidRangeEntry *x = range->entries + i; + UIDRangeEntry *x = range->entries + i; for (size_t j = i + 1; j < range->n_entries; j++) { - UidRangeEntry *y = range->entries + j; + UIDRangeEntry *y = range->entries + j; uid_t begin, end; if (!uid_range_entry_intersect(x, y)) @@ -68,7 +69,7 @@ static void uid_range_coalesce(UidRange *range) { x->nr = end - begin; if (range->n_entries > j + 1) - memmove(y, y + 1, sizeof(UidRangeEntry) * (range->n_entries - j - 1)); + memmove(y, y + 1, sizeof(UIDRangeEntry) * (range->n_entries - j - 1)); range->n_entries--; j--; @@ -76,9 +77,9 @@ static void uid_range_coalesce(UidRange *range) { } } -int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce) { - _cleanup_(uid_range_freep) UidRange *range_new = NULL; - UidRange *p; +int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce) { + _cleanup_(uid_range_freep) UIDRange *range_new = NULL; + UIDRange *p; assert(range); @@ -91,7 +92,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc if (*range) p = *range; else { - range_new = new0(UidRange, 1); + range_new = new0(UIDRange, 1); if (!range_new) return -ENOMEM; @@ -101,7 +102,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc if (!GREEDY_REALLOC(p->entries, p->n_entries + 1)) return -ENOMEM; - p->entries[p->n_entries++] = (UidRangeEntry) { + p->entries[p->n_entries++] = (UIDRangeEntry) { .start = start, .nr = nr, }; @@ -115,7 +116,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc return 0; } -int uid_range_add_str(UidRange **range, const char *s) { +int uid_range_add_str(UIDRange **range, const char *s) { uid_t start, end; int r; @@ -129,7 +130,7 @@ int uid_range_add_str(UidRange **range, const char *s) { return uid_range_add_internal(range, start, end - start + 1, /* coalesce = */ true); } -int uid_range_next_lower(const UidRange *range, uid_t *uid) { +int uid_range_next_lower(const UIDRange *range, uid_t *uid) { uid_t closest = UID_INVALID, candidate; assert(range); @@ -162,7 +163,7 @@ int uid_range_next_lower(const UidRange *range, uid_t *uid) { return 1; } -bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) { +bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr) { if (nr == 0) /* empty range? always covered... */ return true; @@ -172,16 +173,40 @@ bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) { if (!range) return false; - for (size_t i = 0; i < range->n_entries; i++) - if (start >= range->entries[i].start && - start + nr <= range->entries[i].start + range->entries[i].nr) + FOREACH_ARRAY(i, range->entries, range->n_entries) + if (start >= i->start && + start + nr <= i->start + i->nr) return true; return false; } -int uid_range_load_userns(UidRange **ret, const char *path) { - _cleanup_(uid_range_freep) UidRange *range = NULL; +int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) { + uid_t uid_base, uid_shift, uid_range; + int r; + + assert(f); + assert(ret_base); + assert(ret_shift); + assert(ret_range); + + errno = 0; + r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); + if (r == EOF) + return errno_or_else(ENOMSG); + assert(r >= 0); + if (r != 3) + return -EBADMSG; + + *ret_base = uid_base; + *ret_shift = uid_shift; + *ret_range = uid_range; + + return 0; +} + +int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) { + _cleanup_(uid_range_freep) UIDRange *range = NULL; _cleanup_fclose_ FILE *f = NULL; int r; @@ -191,10 +216,12 @@ int uid_range_load_userns(UidRange **ret, const char *path) { * * To simplify things this will modify the passed array in case of later failure. */ + assert(mode >= 0); + assert(mode < _UID_RANGE_USERNS_MODE_MAX); assert(ret); if (!path) - path = "/proc/self/uid_map"; + path = IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "/proc/self/uid_map" : "/proc/self/gid_map"; f = fopen(path, "re"); if (!f) { @@ -206,26 +233,24 @@ int uid_range_load_userns(UidRange **ret, const char *path) { return r; } - range = new0(UidRange, 1); + range = new0(UIDRange, 1); if (!range) return -ENOMEM; for (;;) { uid_t uid_base, uid_shift, uid_range; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); - if (k == EOF) { - if (ferror(f)) - return errno_or_else(EIO); + r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range); + if (r == -ENOMSG) break; - } - if (k != 3) - return -EBADMSG; + if (r < 0) + return r; - r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false); + r = uid_range_add_internal( + &range, + IN_SET(mode, UID_RANGE_USERNS_INSIDE, GID_RANGE_USERNS_INSIDE) ? uid_base : uid_shift, + uid_range, + /* coalesce = */ false); if (r < 0) return r; } @@ -235,3 +260,103 @@ int uid_range_load_userns(UidRange **ret, const char *path) { *ret = TAKE_PTR(range); return 0; } + +int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) { + _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR; + _cleanup_(sigkill_waitp) pid_t pid = 0; + ssize_t n; + char x; + int r; + + assert(userns_fd >= 0); + assert(mode >= 0); + assert(mode < _UID_RANGE_USERNS_MODE_MAX); + assert(ret); + + if (pipe2(pfd, O_CLOEXEC) < 0) + return -errno; + + r = safe_fork_full( + "(sd-mkuserns)", + /* stdio_fds= */ NULL, + (int[]) { pfd[1], userns_fd }, 2, + FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL, + &pid); + if (r < 0) + return r; + if (r == 0) { + /* Child. */ + + if (setns(userns_fd, CLONE_NEWUSER) < 0) { + log_debug_errno(errno, "Failed to join userns: %m"); + _exit(EXIT_FAILURE); + } + + userns_fd = safe_close(userns_fd); + + n = write(pfd[1], &(const char) { 'x' }, 1); + if (n < 0) { + log_debug_errno(errno, "Failed to write to fifo: %m"); + _exit(EXIT_FAILURE); + } + assert(n == 1); + + freeze(); + } + + pfd[1] = safe_close(pfd[1]); + + n = read(pfd[0], &x, 1); + if (n < 0) + return -errno; + if (n == 0) + return -EPROTO; + assert(n == 1); + assert(x == 'x'); + + const char *p = procfs_file_alloca( + pid, + IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map"); + + return uid_range_load_userns(p, mode, ret); +} + +bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) { + + if (!range) + return false; + + /* Avoid overflow */ + if (start > UINT32_MAX - nr) + nr = UINT32_MAX - start; + + if (nr == 0) + return false; + + FOREACH_ARRAY(entry, range->entries, range->n_entries) + if (start < entry->start + entry->nr && + start + nr >= entry->start) + return true; + + return false; +} + +bool uid_range_equal(const UIDRange *a, const UIDRange *b) { + if (a == b) + return true; + + if (!a || !b) + return false; + + if (a->n_entries != b->n_entries) + return false; + + for (size_t i = 0; i < a->n_entries; i++) { + if (a->entries[i].start != b->entries[i].start) + return false; + if (a->entries[i].nr != b->entries[i].nr) + return false; + } + + return true; +} diff --git a/src/basic/uid-range.h b/src/basic/uid-range.h index 461a511..1f687b2 100644 --- a/src/basic/uid-range.h +++ b/src/basic/uid-range.h @@ -6,29 +6,73 @@ #include "macro.h" -typedef struct UidRangeEntry { +typedef struct UIDRangeEntry { uid_t start, nr; -} UidRangeEntry; +} UIDRangeEntry; -typedef struct UidRange { - UidRangeEntry *entries; +typedef struct UIDRange { + UIDRangeEntry *entries; size_t n_entries; -} UidRange; +} UIDRange; -UidRange *uid_range_free(UidRange *range); -DEFINE_TRIVIAL_CLEANUP_FUNC(UidRange*, uid_range_free); +UIDRange *uid_range_free(UIDRange *range); +DEFINE_TRIVIAL_CLEANUP_FUNC(UIDRange*, uid_range_free); -int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce); -static inline int uid_range_add(UidRange **range, uid_t start, uid_t nr) { +int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce); +static inline int uid_range_add(UIDRange **range, uid_t start, uid_t nr) { return uid_range_add_internal(range, start, nr, true); } -int uid_range_add_str(UidRange **range, const char *s); +int uid_range_add_str(UIDRange **range, const char *s); -int uid_range_next_lower(const UidRange *range, uid_t *uid); +int uid_range_next_lower(const UIDRange *range, uid_t *uid); -bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr); -static inline bool uid_range_contains(const UidRange *range, uid_t uid) { +bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr); +static inline bool uid_range_contains(const UIDRange *range, uid_t uid) { return uid_range_covers(range, uid, 1); } -int uid_range_load_userns(UidRange **ret, const char *path); +int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range); + +static inline size_t uid_range_entries(const UIDRange *range) { + return range ? range->n_entries : 0; +} + +static inline unsigned uid_range_size(const UIDRange *range) { + if (!range) + return 0; + + unsigned n = 0; + + FOREACH_ARRAY(e, range->entries, range->n_entries) + n += e->nr; + + return n; +} + +static inline bool uid_range_is_empty(const UIDRange *range) { + + if (!range) + return true; + + FOREACH_ARRAY(e, range->entries, range->n_entries) + if (e->nr > 0) + return false; + + return true; +} + +bool uid_range_equal(const UIDRange *a, const UIDRange *b); + +typedef enum UIDRangeUsernsMode { + UID_RANGE_USERNS_INSIDE, + UID_RANGE_USERNS_OUTSIDE, + GID_RANGE_USERNS_INSIDE, + GID_RANGE_USERNS_OUTSIDE, + _UID_RANGE_USERNS_MODE_MAX, + _UID_RANGE_USERNS_MODE_INVALID = -EINVAL, +} UIDRangeUsernsMode; + +int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret); +int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret); + +bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr); diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index 908c0cd..d03363b 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -99,7 +99,7 @@ static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { [UNIT_BAD_SETTING] = "bad-setting", [UNIT_ERROR] = "error", [UNIT_MERGED] = "merged", - [UNIT_MASKED] = "masked" + [UNIT_MASKED] = "masked", }; DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); @@ -117,14 +117,33 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); static const char* const freezer_state_table[_FREEZER_STATE_MAX] = { - [FREEZER_RUNNING] = "running", - [FREEZER_FREEZING] = "freezing", - [FREEZER_FROZEN] = "frozen", - [FREEZER_THAWING] = "thawing", + [FREEZER_RUNNING] = "running", + [FREEZER_FREEZING] = "freezing", + [FREEZER_FREEZING_BY_PARENT] = "freezing-by-parent", + [FREEZER_FROZEN] = "frozen", + [FREEZER_FROZEN_BY_PARENT] = "frozen-by-parent", + [FREEZER_THAWING] = "thawing", }; DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState); +/* Maps in-progress freezer states to the corresponding finished state */ +static const FreezerState freezer_state_finish_table[_FREEZER_STATE_MAX] = { + [FREEZER_FREEZING] = FREEZER_FROZEN, + [FREEZER_FREEZING_BY_PARENT] = FREEZER_FROZEN_BY_PARENT, + [FREEZER_THAWING] = FREEZER_RUNNING, + + /* Finished states trivially map to themselves */ + [FREEZER_RUNNING] = FREEZER_RUNNING, + [FREEZER_FROZEN] = FREEZER_FROZEN, + [FREEZER_FROZEN_BY_PARENT] = FREEZER_FROZEN_BY_PARENT, +}; + +FreezerState freezer_state_finish(FreezerState state) { + assert(state >= 0 && state < _FREEZER_STATE_MAX); + return freezer_state_finish_table[state]; +} + static const char* const unit_marker_table[_UNIT_MARKER_MAX] = { [UNIT_MARKER_NEEDS_RELOAD] = "needs-reload", [UNIT_MARKER_NEEDS_RESTART] = "needs-restart", @@ -136,7 +155,7 @@ static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { [AUTOMOUNT_DEAD] = "dead", [AUTOMOUNT_WAITING] = "waiting", [AUTOMOUNT_RUNNING] = "running", - [AUTOMOUNT_FAILED] = "failed" + [AUTOMOUNT_FAILED] = "failed", }; DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState); @@ -170,7 +189,7 @@ static const char* const path_state_table[_PATH_STATE_MAX] = { [PATH_DEAD] = "dead", [PATH_WAITING] = "waiting", [PATH_RUNNING] = "running", - [PATH_FAILED] = "failed" + [PATH_FAILED] = "failed", }; DEFINE_STRING_TABLE_LOOKUP(path_state, PathState); @@ -219,7 +238,7 @@ DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState); static const char* const slice_state_table[_SLICE_STATE_MAX] = { [SLICE_DEAD] = "dead", - [SLICE_ACTIVE] = "active" + [SLICE_ACTIVE] = "active", }; DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState); @@ -259,7 +278,7 @@ DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState); static const char* const target_state_table[_TARGET_STATE_MAX] = { [TARGET_DEAD] = "dead", - [TARGET_ACTIVE] = "active" + [TARGET_ACTIVE] = "active", }; DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState); @@ -269,7 +288,7 @@ static const char* const timer_state_table[_TIMER_STATE_MAX] = { [TIMER_WAITING] = "waiting", [TIMER_RUNNING] = "running", [TIMER_ELAPSED] = "elapsed", - [TIMER_FAILED] = "failed" + [TIMER_FAILED] = "failed", }; DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState); @@ -314,7 +333,7 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { [NOTIFY_NONE] = "none", [NOTIFY_MAIN] = "main", [NOTIFY_EXEC] = "exec", - [NOTIFY_ALL] = "all" + [NOTIFY_ALL] = "all", }; DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index 6627da5..8e73e28 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -53,8 +53,10 @@ typedef enum UnitActiveState { typedef enum FreezerState { FREEZER_RUNNING, - FREEZER_FREEZING, + FREEZER_FREEZING, /* freezing due to user request */ FREEZER_FROZEN, + FREEZER_FREEZING_BY_PARENT, /* freezing as a result of parent slice freezing */ + FREEZER_FROZEN_BY_PARENT, FREEZER_THAWING, _FREEZER_STATE_MAX, _FREEZER_STATE_INVALID = -EINVAL, @@ -297,6 +299,7 @@ UnitActiveState unit_active_state_from_string(const char *s) _pure_; const char *freezer_state_to_string(FreezerState i) _const_; FreezerState freezer_state_from_string(const char *s) _pure_; +FreezerState freezer_state_finish(FreezerState i) _const_; const char *unit_marker_to_string(UnitMarker m) _const_; UnitMarker unit_marker_from_string(const char *s) _pure_; diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 8bf28ba..4e2f77c 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -454,34 +454,45 @@ int unit_name_path_unescape(const char *f, char **ret) { return 0; } -int unit_name_replace_instance(const char *f, const char *i, char **ret) { +int unit_name_replace_instance_full( + const char *original, + const char *instance, + bool accept_glob, + char **ret) { + _cleanup_free_ char *s = NULL; - const char *p, *e; - size_t a, b; + const char *prefix, *suffix; + size_t pl; - assert(f); - assert(i); + assert(original); + assert(instance); assert(ret); - if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + if (!unit_name_is_valid(original, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) return -EINVAL; - if (!unit_instance_is_valid(i)) + if (!unit_instance_is_valid(instance) && !(accept_glob && in_charset(instance, VALID_CHARS_GLOB))) return -EINVAL; - assert_se(p = strchr(f, '@')); - assert_se(e = strrchr(f, '.')); + prefix = ASSERT_PTR(strchr(original, '@')); + suffix = ASSERT_PTR(strrchr(original, '.')); + assert(prefix < suffix); - a = p - f; - b = strlen(i); + pl = prefix - original + 1; /* include '@' */ - s = new(char, a + 1 + b + strlen(e) + 1); + s = new(char, pl + strlen(instance) + strlen(suffix) + 1); if (!s) return -ENOMEM; - strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); +#if HAS_FEATURE_MEMORY_SANITIZER + /* MSan doesn't like stpncpy... See also https://github.com/google/sanitizers/issues/926 */ + memzero(s, pl + strlen(instance) + strlen(suffix) + 1); +#endif - /* Make sure the resulting name still is valid, i.e. didn't grow too large */ - if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE)) + strcpy(stpcpy(stpncpy(s, original, pl), instance), suffix); + + /* Make sure the resulting name still is valid, i.e. didn't grow too large. Globs will be expanded + * by clients when used, so the check is pointless. */ + if (!accept_glob && !unit_name_is_valid(s, UNIT_NAME_INSTANCE)) return -EINVAL; *ret = TAKE_PTR(s); @@ -782,19 +793,10 @@ int unit_name_mangle_with_suffix( return 1; good: - s = strdup(name); - if (!s) - return -ENOMEM; - - *ret = TAKE_PTR(s); - return 0; + return strdup_to(ret, name); } int slice_build_parent_slice(const char *slice, char **ret) { - _cleanup_free_ char *s = NULL; - char *dash; - int r; - assert(slice); assert(ret); @@ -806,18 +808,16 @@ int slice_build_parent_slice(const char *slice, char **ret) { return 0; } - s = strdup(slice); + _cleanup_free_ char *s = strdup(slice); if (!s) return -ENOMEM; - dash = strrchr(s, '-'); - if (dash) - strcpy(dash, ".slice"); - else { - r = free_and_strdup(&s, SPECIAL_ROOT_SLICE); - if (r < 0) - return r; - } + char *dash = strrchr(s, '-'); + if (!dash) + return strdup_to_full(ret, SPECIAL_ROOT_SLICE); + + /* We know that s ended with .slice before truncation, so we have enough space. */ + strcpy(dash, ".slice"); *ret = TAKE_PTR(s); return 1; diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index eaa701e..fa7295e 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -33,14 +33,21 @@ UnitType unit_name_to_type(const char *n) _pure_; int unit_name_change_suffix(const char *n, const char *suffix, char **ret); int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret); -int unit_name_build_from_type(const char *prefix, const char *instance, UnitType, char **ret); +int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret); char *unit_name_escape(const char *f); int unit_name_unescape(const char *f, char **ret); int unit_name_path_escape(const char *f, char **ret); int unit_name_path_unescape(const char *f, char **ret); -int unit_name_replace_instance(const char *f, const char *i, char **ret); +int unit_name_replace_instance_full( + const char *original, + const char *instance, + bool accept_glob, + char **ret); +static inline int unit_name_replace_instance(const char *original, const char *instance, char **ret) { + return unit_name_replace_instance_full(original, instance, false, ret); +} int unit_name_template(const char *f, char **ret); diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 9e6926b..6bdf5bf 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -184,27 +184,28 @@ const char* default_root_shell(const char *root) { static int synthesize_user_creds( const char **username, - uid_t *uid, gid_t *gid, - const char **home, - const char **shell, + uid_t *ret_uid, gid_t *ret_gid, + const char **ret_home, + const char **ret_shell, UserCredsFlags flags) { + assert(username); + assert(*username); + /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode * their user record data. */ if (STR_IN_SET(*username, "root", "0")) { *username = "root"; - if (uid) - *uid = 0; - if (gid) - *gid = 0; - - if (home) - *home = "/root"; - - if (shell) - *shell = default_root_shell(NULL); + if (ret_uid) + *ret_uid = 0; + if (ret_gid) + *ret_gid = 0; + if (ret_home) + *ret_home = "/root"; + if (ret_shell) + *ret_shell = default_root_shell(NULL); return 0; } @@ -213,16 +214,14 @@ static int synthesize_user_creds( synthesize_nobody()) { *username = NOBODY_USER_NAME; - if (uid) - *uid = UID_NOBODY; - if (gid) - *gid = GID_NOBODY; - - if (home) - *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/"; - - if (shell) - *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN; + if (ret_uid) + *ret_uid = UID_NOBODY; + if (ret_gid) + *ret_gid = GID_NOBODY; + if (ret_home) + *ret_home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/"; + if (ret_shell) + *ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN; return 0; } @@ -232,11 +231,12 @@ static int synthesize_user_creds( int get_user_creds( const char **username, - uid_t *uid, gid_t *gid, - const char **home, - const char **shell, + uid_t *ret_uid, gid_t *ret_gid, + const char **ret_home, + const char **ret_shell, UserCredsFlags flags) { + bool patch_username = false; uid_t u = UID_INVALID; struct passwd *p; int r; @@ -245,7 +245,7 @@ int get_user_creds( assert(*username); if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) || - (!home && !shell)) { + (!ret_home && !ret_shell)) { /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the @@ -256,7 +256,7 @@ int get_user_creds( * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't * support. */ - r = synthesize_user_creds(username, uid, gid, home, shell, flags); + r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags); if (r >= 0) return 0; if (r != -ENOMEDIUM) /* not a username we can synthesize */ @@ -271,15 +271,15 @@ int get_user_creds( * instead of the first occurrence in the database. However if the uid was configured by a numeric uid, * then let's pick the real username from /etc/passwd. */ if (p) - *username = p->pw_name; - else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) { + patch_username = true; + else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) { /* If the specified user is a numeric UID and it isn't in the user database, and the caller * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that * and don't complain. */ - if (uid) - *uid = u; + if (ret_uid) + *ret_uid = u; return 0; } @@ -293,65 +293,57 @@ int get_user_creds( r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno; /* If the user requested that we only synthesize as fallback, do so now */ - if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) { - if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0) + if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) + if (synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags) >= 0) return 0; - } return r; } - if (uid) { - if (!uid_is_valid(p->pw_uid)) - return -EBADMSG; + if (ret_uid && !uid_is_valid(p->pw_uid)) + return -EBADMSG; - *uid = p->pw_uid; - } + if (ret_gid && !gid_is_valid(p->pw_gid)) + return -EBADMSG; - if (gid) { - if (!gid_is_valid(p->pw_gid)) - return -EBADMSG; + if (ret_uid) + *ret_uid = p->pw_uid; - *gid = p->pw_gid; - } + if (ret_gid) + *ret_gid = p->pw_gid; - if (home) { - if (FLAGS_SET(flags, USER_CREDS_CLEAN) && - (empty_or_root(p->pw_dir) || - !path_is_valid(p->pw_dir) || - !path_is_absolute(p->pw_dir))) - *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */ - else - *home = p->pw_dir; - } + if (ret_home) + /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */ + *ret_home = (FLAGS_SET(flags, USER_CREDS_CLEAN) && + (empty_or_root(p->pw_dir) || + !path_is_valid(p->pw_dir) || + !path_is_absolute(p->pw_dir))) ? NULL : p->pw_dir; - if (shell) { - if (FLAGS_SET(flags, USER_CREDS_CLEAN) && - (isempty(p->pw_shell) || - !path_is_valid(p->pw_shell) || - !path_is_absolute(p->pw_shell) || - is_nologin_shell(p->pw_shell))) - *shell = NULL; - else - *shell = p->pw_shell; - } + if (ret_shell) + *ret_shell = (FLAGS_SET(flags, USER_CREDS_CLEAN) && + (isempty(p->pw_shell) || + !path_is_valid(p->pw_shell) || + !path_is_absolute(p->pw_shell) || + is_nologin_shell(p->pw_shell))) ? NULL : p->pw_shell; + + if (patch_username) + *username = p->pw_name; return 0; } -int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) { - struct group *g; - gid_t id; +static int synthesize_group_creds( + const char **groupname, + gid_t *ret_gid) { assert(groupname); - - /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */ + assert(*groupname); if (STR_IN_SET(*groupname, "root", "0")) { *groupname = "root"; - if (gid) - *gid = 0; + if (ret_gid) + *ret_gid = 0; return 0; } @@ -360,21 +352,41 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) { synthesize_nobody()) { *groupname = NOBODY_GROUP_NAME; - if (gid) - *gid = GID_NOBODY; + if (ret_gid) + *ret_gid = GID_NOBODY; return 0; } + return -ENOMEDIUM; +} + +int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) { + bool patch_groupname = false; + struct group *g; + gid_t id; + int r; + + assert(groupname); + assert(*groupname); + + if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) { + r = synthesize_group_creds(groupname, ret_gid); + if (r >= 0) + return 0; + if (r != -ENOMEDIUM) /* not a groupname we can synthesize */ + return r; + } + if (parse_gid(*groupname, &id) >= 0) { errno = 0; g = getgrgid(id); if (g) - *groupname = g->gr_name; + patch_groupname = true; else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) { - if (gid) - *gid = id; + if (ret_gid) + *ret_gid = id; return 0; } @@ -383,18 +395,28 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) { g = getgrnam(*groupname); } - if (!g) + if (!g) { /* getgrnam() may fail with ENOENT if /etc/group is missing. * For us that is equivalent to the name not being defined. */ - return IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno; + r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno; + + if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) + if (synthesize_group_creds(groupname, ret_gid) >= 0) + return 0; - if (gid) { + return r; + } + + if (ret_gid) { if (!gid_is_valid(g->gr_gid)) return -EBADMSG; - *gid = g->gr_gid; + *ret_gid = g->gr_gid; } + if (patch_groupname) + *groupname = g->gr_name; + return 0; } @@ -409,31 +431,11 @@ char* uid_to_name(uid_t uid) { return strdup(NOBODY_USER_NAME); if (uid_is_valid(uid)) { - long bufsize; - - bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); - if (bufsize <= 0) - bufsize = 4096; + _cleanup_free_ struct passwd *pw = NULL; - for (;;) { - struct passwd pwbuf, *pw = NULL; - _cleanup_free_ char *buf = NULL; - - buf = malloc(bufsize); - if (!buf) - return NULL; - - r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw); - if (r == 0 && pw) - return strdup(pw->pw_name); - if (r != ERANGE) - break; - - if (bufsize > LONG_MAX/2) /* overflow check */ - return NULL; - - bufsize *= 2; - } + r = getpwuid_malloc(uid, &pw); + if (r >= 0) + return strdup(pw->pw_name); } if (asprintf(&ret, UID_FMT, uid) < 0) @@ -452,31 +454,11 @@ char* gid_to_name(gid_t gid) { return strdup(NOBODY_GROUP_NAME); if (gid_is_valid(gid)) { - long bufsize; - - bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); - if (bufsize <= 0) - bufsize = 4096; - - for (;;) { - struct group grbuf, *gr = NULL; - _cleanup_free_ char *buf = NULL; - - buf = malloc(bufsize); - if (!buf) - return NULL; - - r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr); - if (r == 0 && gr) - return strdup(gr->gr_name); - if (r != ERANGE) - break; - - if (bufsize > LONG_MAX/2) /* overflow check */ - return NULL; + _cleanup_free_ struct group *gr = NULL; - bufsize *= 2; - } + r = getgrgid_malloc(gid, &gr); + if (r >= 0) + return strdup(gr->gr_name); } if (asprintf(&ret, GID_FMT, gid) < 0) @@ -587,9 +569,10 @@ int getgroups_alloc(gid_t** gids) { } int get_home_dir(char **ret) { - struct passwd *p; + _cleanup_free_ struct passwd *p = NULL; const char *e; uid_t u; + int r; assert(ret); @@ -604,19 +587,17 @@ int get_home_dir(char **ret) { e = "/root"; goto found; } - if (u == UID_NOBODY && synthesize_nobody()) { e = "/"; goto found; } /* Check the database... */ - errno = 0; - p = getpwuid(u); - if (!p) - return errno_or_else(ESRCH); - e = p->pw_dir; + r = getpwuid_malloc(u, &p); + if (r < 0) + return r; + e = p->pw_dir; if (!path_is_valid(e) || !path_is_absolute(e)) return -EINVAL; @@ -625,9 +606,10 @@ int get_home_dir(char **ret) { } int get_shell(char **ret) { - struct passwd *p; + _cleanup_free_ struct passwd *p = NULL; const char *e; uid_t u; + int r; assert(ret); @@ -648,12 +630,11 @@ int get_shell(char **ret) { } /* Check the database... */ - errno = 0; - p = getpwuid(u); - if (!p) - return errno_or_else(ESRCH); - e = p->pw_shell; + r = getpwuid_malloc(u, &p); + if (r < 0) + return r; + e = p->pw_shell; if (!path_is_valid(e) || !path_is_absolute(e)) return -EINVAL; @@ -661,17 +642,26 @@ int get_shell(char **ret) { return path_simplify_alloc(e, ret); } -int reset_uid_gid(void) { +int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) { int r; - r = maybe_setgroups(0, NULL); + assert(supplementary_gids || n_supplementary_gids == 0); + + /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */ + + r = maybe_setgroups(n_supplementary_gids, supplementary_gids); if (r < 0) return r; - if (setresgid(0, 0, 0) < 0) - return -errno; + if (gid_is_valid(gid)) + if (setresgid(gid, gid, gid) < 0) + return -errno; + + if (uid_is_valid(uid)) + if (setresuid(uid, uid, uid) < 0) + return -errno; - return RET_NERRNO(setresuid(0, 0, 0)); + return 0; } int take_etc_passwd_lock(const char *root) { @@ -807,11 +797,11 @@ bool valid_user_group_name(const char *u, ValidUserFlags flags) { sz = sysconf(_SC_LOGIN_NAME_MAX); assert_se(sz > 0); - if (l > (size_t) sz) + if (l > (size_t) sz) /* glibc: 256 */ return false; - if (l > NAME_MAX) /* must fit in a filename */ + if (l > NAME_MAX) /* must fit in a filename: 255 */ return false; - if (l > UT_NAMESIZE - 1) + if (l > UT_NAMESIZE - 1) /* must fit in utmp: 31 */ return false; } @@ -987,8 +977,8 @@ int fgetpwent_sane(FILE *stream, struct passwd **pw) { errno = 0; struct passwd *p = fgetpwent(stream); - if (!p && errno != ENOENT) - return errno_or_else(EIO); + if (!p && !IN_SET(errno, 0, ENOENT)) + return -errno; *pw = p; return !!p; @@ -1000,8 +990,8 @@ int fgetspent_sane(FILE *stream, struct spwd **sp) { errno = 0; struct spwd *s = fgetspent(stream); - if (!s && errno != ENOENT) - return errno_or_else(EIO); + if (!s && !IN_SET(errno, 0, ENOENT)) + return -errno; *sp = s; return !!s; @@ -1013,8 +1003,8 @@ int fgetgrent_sane(FILE *stream, struct group **gr) { errno = 0; struct group *g = fgetgrent(stream); - if (!g && errno != ENOENT) - return errno_or_else(EIO); + if (!g && !IN_SET(errno, 0, ENOENT)) + return -errno; *gr = g; return !!g; @@ -1027,8 +1017,8 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg) { errno = 0; struct sgrp *s = fgetsgent(stream); - if (!s && errno != ENOENT) - return errno_or_else(EIO); + if (!s && !IN_SET(errno, 0, ENOENT)) + return -errno; *sg = s; return !!s; @@ -1058,3 +1048,180 @@ const char* get_home_root(void) { return "/home"; } + +static size_t getpw_buffer_size(void) { + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + return bufsize <= 0 ? 4096U : (size_t) bufsize; +} + +static bool errno_is_user_doesnt_exist(int error) { + /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are + * not found. */ + return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM); +} + +int getpwnam_malloc(const char *name, struct passwd **ret) { + size_t bufsize = getpw_buffer_size(); + int r; + + /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must + * free() the returned structures! */ + + if (isempty(name)) + return -EINVAL; + + for (;;) { + _cleanup_free_ void *buf = NULL; + + buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize); + if (!buf) + return -ENOMEM; + + struct passwd *pw = NULL; + r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw); + if (r == 0) { + if (pw) { + if (ret) + *ret = TAKE_PTR(buf); + return 0; + } + + return -ESRCH; + } + + assert(r > 0); + + /* getpwnam() may fail with ENOENT if /etc/passwd is missing. For us that is equivalent to + * the name not being defined. */ + if (errno_is_user_doesnt_exist(r)) + return -ESRCH; + if (r != ERANGE) + return -r; + + if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd))) + return -ENOMEM; + bufsize *= 2; + } +} + +int getpwuid_malloc(uid_t uid, struct passwd **ret) { + size_t bufsize = getpw_buffer_size(); + int r; + + if (!uid_is_valid(uid)) + return -EINVAL; + + for (;;) { + _cleanup_free_ void *buf = NULL; + + buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize); + if (!buf) + return -ENOMEM; + + struct passwd *pw = NULL; + r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw); + if (r == 0) { + if (pw) { + if (ret) + *ret = TAKE_PTR(buf); + return 0; + } + + return -ESRCH; + } + + assert(r > 0); + + if (errno_is_user_doesnt_exist(r)) + return -ESRCH; + if (r != ERANGE) + return -r; + + if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd))) + return -ENOMEM; + bufsize *= 2; + } +} + +static size_t getgr_buffer_size(void) { + long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); + return bufsize <= 0 ? 4096U : (size_t) bufsize; +} + +int getgrnam_malloc(const char *name, struct group **ret) { + size_t bufsize = getgr_buffer_size(); + int r; + + if (isempty(name)) + return -EINVAL; + + for (;;) { + _cleanup_free_ void *buf = NULL; + + buf = malloc(ALIGN(sizeof(struct group)) + bufsize); + if (!buf) + return -ENOMEM; + + struct group *gr = NULL; + r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr); + if (r == 0) { + if (gr) { + if (ret) + *ret = TAKE_PTR(buf); + return 0; + } + + return -ESRCH; + } + + assert(r > 0); + + if (errno_is_user_doesnt_exist(r)) + return -ESRCH; + if (r != ERANGE) + return -r; + + if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group))) + return -ENOMEM; + bufsize *= 2; + } +} + +int getgrgid_malloc(gid_t gid, struct group **ret) { + size_t bufsize = getgr_buffer_size(); + int r; + + if (!gid_is_valid(gid)) + return -EINVAL; + + for (;;) { + _cleanup_free_ void *buf = NULL; + + buf = malloc(ALIGN(sizeof(struct group)) + bufsize); + if (!buf) + return -ENOMEM; + + struct group *gr = NULL; + r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr); + if (r == 0) { + if (gr) { + if (ret) + *ret = TAKE_PTR(buf); + return 0; + } + + return -ESRCH; + } + + assert(r > 0); + + if (errno_is_user_doesnt_exist(r)) + return -ESRCH; + if (r != ERANGE) + return -r; + + if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group))) + return -ENOMEM; + bufsize *= 2; + } +} diff --git a/src/basic/user-util.h b/src/basic/user-util.h index f394f62..9d07ef3 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -42,8 +42,8 @@ typedef enum UserCredsFlags { USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */ } UserCredsFlags; -int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags); -int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags); +int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags); +int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags); char* uid_to_name(uid_t uid); char* gid_to_name(gid_t gid); @@ -57,7 +57,10 @@ int getgroups_alloc(gid_t** gids); int get_home_dir(char **ret); int get_shell(char **ret); -int reset_uid_gid(void); +int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids); +static inline int reset_uid_gid(void) { + return fully_set_uid_gid(0, 0, NULL, 0); +} int take_etc_passwd_lock(const char *root); @@ -155,3 +158,9 @@ static inline bool hashed_password_is_locked_or_invalid(const char *password) { * Also see https://github.com/systemd/systemd/pull/24680#pullrequestreview-1439464325. */ #define PASSWORD_UNPROVISIONED "!unprovisioned" + +int getpwuid_malloc(uid_t uid, struct passwd **ret); +int getpwnam_malloc(const char *name, struct passwd **ret); + +int getgrnam_malloc(const char *name, struct group **ret); +int getgrgid_malloc(gid_t gid, struct group **ret); diff --git a/src/basic/utf8.c b/src/basic/utf8.c index 36e1e0f..36f0dc9 100644 --- a/src/basic/utf8.c +++ b/src/basic/utf8.c @@ -1,26 +1,10 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-License-Identifier: LGPL-2.0-or-later */ -/* Parts of this file are based on the GLIB utf8 validation functions. The - * original license text follows. */ - -/* gutf8.c - Operations on UTF-8 strings. +/* Parts of this file are based on the GLIB utf8 validation functions. The original copyright follows. * + * gutf8.c - Operations on UTF-8 strings. * Copyright (C) 1999 Tom Tromey * Copyright (C) 2000 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <errno.h> diff --git a/src/basic/virt.c b/src/basic/virt.c index 88357a9..0970350 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -21,6 +21,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "uid-range.h" #include "virt.h" enum { @@ -446,7 +447,7 @@ static Virtualization detect_vm_zvm(void) { /* Returns a short identifier for the various VM implementations */ Virtualization detect_vm(void) { static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID; - bool other = false; + bool other = false, hyperv = false; int xen_dom0 = 0; Virtualization v, dmi; @@ -455,12 +456,12 @@ Virtualization detect_vm(void) { /* We have to use the correct order here: * - * → First, try to detect Oracle Virtualbox, Amazon EC2 Nitro, Parallels, and Google Compute Engine, even if they use KVM, - * as well as Xen even if it cloaks as Microsoft Hyper-V. Attempt to detect uml at this stage also - * since it runs as a user-process nested inside other VMs. Also check for Xen now, because Xen PV - * mode does not override CPUID when nested inside another hypervisor. + * → First, try to detect Oracle Virtualbox, Amazon EC2 Nitro, Parallels, and Google Compute Engine, + * even if they use KVM, as well as Xen, even if it cloaks as Microsoft Hyper-V. Attempt to detect + * UML at this stage too, since it runs as a user-process nested inside other VMs. Also check for + * Xen now, because Xen PV mode does not override CPUID when nested inside another hypervisor. * - * → Second, try to detect from CPUID, this will report KVM for whatever software is used even if + * → Second, try to detect from CPUID. This will report KVM for whatever software is used even if * info in DMI is overwritten. * * → Third, try to detect from DMI. */ @@ -503,7 +504,12 @@ Virtualization detect_vm(void) { v = detect_vm_cpuid(); if (v < 0) return v; - if (v == VIRTUALIZATION_VM_OTHER) + if (v == VIRTUALIZATION_MICROSOFT) + /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's + * hence not return Microsoft here but just use the other mechanisms first to make a better + * decision. */ + hyperv = true; + else if (v == VIRTUALIZATION_VM_OTHER) other = true; else if (v != VIRTUALIZATION_NONE) goto finish; @@ -544,8 +550,15 @@ Virtualization detect_vm(void) { return v; finish: - if (v == VIRTUALIZATION_NONE && other) - v = VIRTUALIZATION_VM_OTHER; + /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv + * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably + * Microsoft. */ + if (v == VIRTUALIZATION_NONE) { + if (hyperv) + v = VIRTUALIZATION_MICROSOFT; + else if (other) + v = VIRTUALIZATION_VM_OTHER; + } cached_found = v; log_debug("Found VM virtualization %s", virtualization_to_string(v)); @@ -818,7 +831,7 @@ Virtualization detect_virtualization(void) { static int userns_has_mapping(const char *name) { _cleanup_fclose_ FILE *f = NULL; - uid_t a, b, c; + uid_t base, shift, range; int r; f = fopen(name, "re"); @@ -827,26 +840,22 @@ static int userns_has_mapping(const char *name) { return errno == ENOENT ? false : -errno; } - errno = 0; - r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &a, &b, &c); - if (r == EOF) { - if (ferror(f)) - return log_debug_errno(errno_or_else(EIO), "Failed to read %s: %m", name); - - log_debug("%s is empty, we're in an uninitialized user namespace", name); + r = uid_map_read_one(f, &base, &shift, &range); + if (r == -ENOMSG) { + log_debug("%s is empty, we're in an uninitialized user namespace.", name); return true; } - if (r != 3) - return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse %s: %m", name); + if (r < 0) + return log_debug_errno(r, "Failed to read %s: %m", name); - if (a == 0 && b == 0 && c == UINT32_MAX) { + if (base == 0 && shift == 0 && range == UINT32_MAX) { /* The kernel calls mappings_overlap() and does not allow overlaps */ log_debug("%s has a full 1:1 mapping", name); return false; } /* Anything else implies that we are in a user namespace */ - log_debug("Mapping found in %s, we're in a user namespace", name); + log_debug("Mapping found in %s, we're in a user namespace.", name); return true; } |