diff options
Diffstat (limited to 'shared/systemd/src/basic')
48 files changed, 1798 insertions, 874 deletions
diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c index 30ee135a..1e93f18e 100644 --- a/shared/systemd/src/basic/env-file.c +++ b/shared/systemd/src/basic/env-file.c @@ -211,17 +211,21 @@ static int parse_env_file_internal( case DOUBLE_QUOTE_VALUE_ESCAPE: state = DOUBLE_QUOTE_VALUE; - if (c == '"') { + if (strchr(SHELL_NEED_ESCAPE, c)) { + /* If this is a char that needs escaping, just unescape it. */ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) return -ENOMEM; - value[n_value++] = '"'; - } else if (!strchr(NEWLINE, c)) { + value[n_value++] = c; + } else if (c != '\n') { + /* If other char than what needs escaping, keep the "\" in place, like the + * real shell does. */ if (!GREEDY_REALLOC(value, value_alloc, n_value+3)) return -ENOMEM; value[n_value++] = '\\'; value[n_value++] = c; } + /* Escaped newlines (aka "continuation lines") are eaten up entirely */ break; case COMMENT: diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c index d16890d9..11f4b29b 100644 --- a/shared/systemd/src/basic/env-util.c +++ b/shared/systemd/src/basic/env-util.c @@ -690,7 +690,7 @@ char **replace_env_argv(char **argv, char **env) { if (e) { int r; - r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE); + r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE); if (r < 0) { ret[k] = NULL; strv_free(ret); @@ -744,7 +744,6 @@ int getenv_bool(const char *p) { return parse_boolean(e); } -#if 0 /* NM_IGNORED */ int getenv_bool_secure(const char *p) { const char *e; @@ -754,4 +753,3 @@ int getenv_bool_secure(const char *p) { return parse_boolean(e); } -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c index 1ea0b6a6..1261fd48 100644 --- a/shared/systemd/src/basic/escape.c +++ b/shared/systemd/src/basic/escape.c @@ -77,7 +77,7 @@ int cescape_char(char c, char *buf) { return buf - buf_old; } -char *cescape_length(const char *s, size_t n) { +char* cescape_length(const char *s, size_t n) { const char *f; char *r, *t; @@ -98,7 +98,7 @@ char *cescape_length(const char *s, size_t n) { return r; } -char *cescape(const char *s) { +char* cescape(const char *s) { assert(s); return cescape_length(s, strlen(s)); @@ -363,7 +363,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi return t - r; } -char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) { +char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) { char *ans, *t, *prev, *prev2; const char *f; @@ -430,7 +430,7 @@ char *xescape_full(const char *s, const char *bad, size_t console_width, bool ei return ans; } -char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) { +char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) { if (eight_bit) return xescape_full(str, "", console_width, true); else @@ -438,7 +438,7 @@ char *escape_non_printable_full(const char *str, size_t console_width, bool eigh } #endif /* NM_IGNORED */ -char *octescape(const char *s, size_t len) { +char* octescape(const char *s, size_t len) { char *r, *t; const char *f; @@ -466,7 +466,7 @@ char *octescape(const char *s, size_t len) { } -static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { +static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { assert(bad); for (; *s; s++) { @@ -485,7 +485,7 @@ static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, b return t; } -char *shell_escape(const char *s, const char *bad) { +char* shell_escape(const char *s, const char *bad) { char *r, *t; r = new(char, strlen(s)*2+1); diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h index 0b00b116..fa267813 100644 --- a/shared/systemd/src/basic/escape.h +++ b/shared/systemd/src/basic/escape.h @@ -43,8 +43,8 @@ typedef enum EscapeStyle { * syntax (a string enclosed in $'') instead of plain quotes. */ } EscapeStyle; -char *cescape(const char *s); -char *cescape_length(const char *s, size_t n); +char* cescape(const char *s); +char* cescape_length(const char *s, size_t n); int cescape_char(char c, char *buf); int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret); @@ -56,12 +56,12 @@ static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) { } int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul); -char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits); -static inline char *xescape(const char *s, const char *bad) { +char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits); +static inline char* xescape(const char *s, const char *bad) { return xescape_full(s, bad, SIZE_MAX, false); } -char *octescape(const char *s, size_t len); -char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit); +char* octescape(const char *s, size_t len); +char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit); -char *shell_escape(const char *s, const char *bad); +char* shell_escape(const char *s, const char *bad); char* shell_maybe_quote(const char *s, EscapeStyle style); diff --git a/shared/systemd/src/basic/extract-word.c b/shared/systemd/src/basic/extract-word.c index 62f015f3..e32b4c7f 100644 --- a/shared/systemd/src/basic/extract-word.c +++ b/shared/systemd/src/basic/extract-word.c @@ -16,6 +16,7 @@ #include "log.h" #include "macro.h" #include "string-util.h" +#include "strv.h" #include "utf8.h" int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { @@ -88,25 +89,30 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra return -EINVAL; } - if (flags & EXTRACT_CUNESCAPE) { + if (flags & (EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS)) { bool eight_bit = false; char32_t u; - r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false); - if (r < 0) { - if (flags & EXTRACT_CUNESCAPE_RELAX) { - s[sz++] = '\\'; - s[sz++] = c; - } else - return -EINVAL; - } else { + if ((flags & EXTRACT_CUNESCAPE) && + (r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false)) >= 0) { + /* A valid escaped sequence */ + assert(r >= 1); + (*p) += r - 1; if (eight_bit) s[sz++] = u; else sz += utf8_encode_unichar(s + sz, u); - } + } else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) && + strchr(separators, **p)) + /* An escaped separator char */ + s[sz++] = c; + else if (flags & EXTRACT_CUNESCAPE_RELAX) { + s[sz++] = '\\'; + s[sz++] = c; + } else + return -EINVAL; } else s[sz++] = c; diff --git a/shared/systemd/src/basic/extract-word.h b/shared/systemd/src/basic/extract-word.h index e2d43389..f028577c 100644 --- a/shared/systemd/src/basic/extract-word.h +++ b/shared/systemd/src/basic/extract-word.h @@ -7,9 +7,10 @@ typedef enum ExtractFlags { EXTRACT_RELAX = 1 << 0, EXTRACT_CUNESCAPE = 1 << 1, EXTRACT_CUNESCAPE_RELAX = 1 << 2, - EXTRACT_UNQUOTE = 1 << 3, - EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4, - EXTRACT_RETAIN_ESCAPE = 1 << 5, + EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, + EXTRACT_UNQUOTE = 1 << 4, + EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, + EXTRACT_RETAIN_ESCAPE = 1 << 6, } ExtractFlags; int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c index a401663e..525d3828 100644 --- a/shared/systemd/src/basic/fd-util.c +++ b/shared/systemd/src/basic/fd-util.c @@ -379,6 +379,7 @@ bool fdname_is_valid(const char *s) { return p - s < 256; } +#if 0 /* NM_IGNORED */ int fd_get_path(int fd, char **ret) { char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; int r; @@ -389,18 +390,15 @@ int fd_get_path(int fd, char **ret) { /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make * things debuggable and distinguish the two. */ - if (access("/proc/self/fd/", F_OK) < 0) - /* /proc is not available or not set up properly, we're most likely in some chroot - * environment. */ - return errno == ENOENT ? -EOPNOTSUPP : -errno; - + if (proc_mounted() == 0) + return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot + * environment. */ return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ } return r; } -#if 0 /* NM_IGNORED */ int move_fd(int from, int to, int cloexec) { int r; @@ -736,8 +734,7 @@ int fd_duplicate_data_fd(int fd) { if (f != 0) return -errno; - safe_close(copy_fd); - copy_fd = TAKE_FD(tmp_fd); + CLOSE_AND_REPLACE(copy_fd, tmp_fd); remains = mfree(remains); remains_size = 0; @@ -872,8 +869,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ goto finish; } - safe_close(null_fd); - null_fd = copy; + CLOSE_AND_REPLACE(null_fd, copy); } } @@ -983,7 +979,7 @@ int read_nr_open(void) { return v; } - /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ + /* If we fail, fall back to the hard-coded kernel limit of 1024 * 1024. */ return 1024 * 1024; } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fd-util.h b/shared/systemd/src/basic/fd-util.h index e490753c..93ce95cd 100644 --- a/shared/systemd/src/basic/fd-util.h +++ b/shared/systemd/src/basic/fd-util.h @@ -93,6 +93,16 @@ static inline int make_null_stdio(void) { _fd_; \ }) +/* Like free_and_replace(), but for file descriptors */ +#define CLOSE_AND_REPLACE(a, b) \ + ({ \ + int *_fdp_ = &(a); \ + safe_close(*_fdp_); \ + *_fdp_ = TAKE_FD(b); \ + 0; \ + }) + + int fd_reopen(int fd, int flags); int read_nr_open(void); diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c index 30903263..664b894a 100644 --- a/shared/systemd/src/basic/fileio.c +++ b/shared/systemd/src/basic/fileio.c @@ -24,6 +24,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "socket-util.h" #include "stdio-util.h" #include "string-util.h" #include "tmpfile-util.h" @@ -486,13 +487,12 @@ int read_full_stream_full( assert(f); assert(ret_contents); assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)); - assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size); n_next = LINE_MAX; /* Start size */ fd = fileno(f); - if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's - * optimize our buffering) */ + if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's + * optimize our buffering */ if (fstat(fd, &st) < 0) return -errno; @@ -509,7 +509,7 @@ int read_full_stream_full( if (st.st_size > 0) n_next = st.st_size + 1; - if (flags & READ_FULL_FILE_SECURE) + if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) (void) warn_file_is_world_accessible(filename, &st, NULL, 0); } } @@ -539,21 +539,18 @@ int read_full_stream_full( errno = 0; k = fread(buf + l, 1, n - l, f); - if (k > 0) - l += k; + + assert(k <= n - l); + l += k; if (ferror(f)) { r = errno_or_else(EIO); goto finalize; } - if (feof(f)) break; - /* We aren't expecting fread() to return a short read outside - * of (error && eof), assert buffer is full and enlarge buffer. - */ - assert(l == n); + assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ /* Safety check */ if (n >= READ_FULL_BYTES_MAX) { @@ -565,12 +562,21 @@ int read_full_stream_full( } if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) { + _cleanup_free_ void *decoded = NULL; + size_t decoded_size; + buf[l++] = 0; if (flags & READ_FULL_FILE_UNBASE64) - r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); + r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); else - r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); - goto finalize; + r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); + if (r < 0) + goto finalize; + + if (flags & READ_FULL_FILE_SECURE) + explicit_bzero_safe(buf, n); + free_and_replace(buf, decoded); + n = l = decoded_size; } if (!ret_size) { @@ -607,8 +613,54 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag assert(contents); r = xfopenat(dir_fd, filename, "re", 0, &f); - if (r < 0) - return r; + if (r < 0) { + _cleanup_close_ int dfd = -1, sk = -1; + union sockaddr_union sa; + + /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ + if (r != -ENXIO) + return r; + + /* If this is enabled, let's try to connect to it */ + if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) + return -ENXIO; + + if (dir_fd == AT_FDCWD) + r = sockaddr_un_set_path(&sa.un, filename); + else { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + /* If we shall operate relative to some directory, then let's use O_PATH first to + * open the socket inode, and then connect to it via /proc/self/fd/. We have to do + * this since there's not connectat() that takes a directory fd as first arg. */ + + dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC); + if (dfd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", dfd); + r = sockaddr_un_set_path(&sa.un, procfs_path); + } + if (r < 0) + return r; + + sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (sk < 0) + return -errno; + + if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is + * not a socket after all */ + + if (shutdown(sk, SHUT_WR) < 0) + return -errno; + + f = fdopen(sk, "r"); + if (!f) + return -errno; + + TAKE_FD(sk); + } (void) __fsetlocking(f, FSETLOCKING_BYCALLER); @@ -894,6 +946,42 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root return search_and_fopen_internal(path, mode, root, s, _f); } + +int chase_symlinks_and_fopen_unlocked( + const char *path, + const char *root, + unsigned chase_flags, + const char *open_flags, + FILE **ret_file, + char **ret_path) { + + _cleanup_close_ int fd = -1; + _cleanup_free_ char *final_path = NULL; + int mode_flags, r; + FILE *f; + + assert(path); + assert(open_flags); + assert(ret_file); + + mode_flags = mode_to_flags(open_flags); + if (mode_flags < 0) + return mode_flags; + + fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL); + if (fd < 0) + return fd; + + r = fdopen_unlocked(fd, open_flags, &f); + if (r < 0) + return r; + TAKE_FD(fd); + + *ret_file = f; + if (ret_path) + *ret_path = TAKE_PTR(final_path); + return 0; +} #endif /* NM_IGNORED */ int fflush_and_check(FILE *f) { diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h index e2830b79..9cba5a90 100644 --- a/shared/systemd/src/basic/fileio.h +++ b/shared/systemd/src/basic/fileio.h @@ -32,9 +32,11 @@ typedef enum { } WriteStringFileFlags; typedef enum { - READ_FULL_FILE_SECURE = 1 << 0, - READ_FULL_FILE_UNBASE64 = 1 << 1, - READ_FULL_FILE_UNHEX = 1 << 2, + READ_FULL_FILE_SECURE = 1 << 0, /* erase any buffers we employ internally, after use */ + READ_FULL_FILE_UNBASE64 = 1 << 1, /* base64 decode what we read */ + READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */ + READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */ + READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */ } ReadFullFileFlags; int fopen_unlocked(const char *path, const char *options, FILE **ret); @@ -79,6 +81,14 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); +int chase_symlinks_and_fopen_unlocked( + const char *path, + const char *root, + unsigned chase_flags, + const char *open_flags, + FILE **ret_file, + char **ret_path); + int fflush_and_check(FILE *f); int fflush_sync_and_check(FILE *f); diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c index 75df913a..e50252cb 100644 --- a/shared/systemd/src/basic/fs-util.c +++ b/shared/systemd/src/basic/fs-util.c @@ -123,7 +123,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */ return -errno; - /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fallback to the racy TOCTOU + /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fall back to the racy TOCTOU * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */ if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) @@ -234,6 +234,7 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { bool do_chown, do_chmod; struct stat st; + int r; /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no * point in time the access mode is above the old access mode under the old ownership or the new @@ -264,65 +265,23 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { if (do_chown && do_chmod) { mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */ - if (((minimal ^ st.st_mode) & 07777) != 0) - if (fchmod_opath(fd, minimal & 07777) < 0) - return -errno; + if (((minimal ^ st.st_mode) & 07777) != 0) { + r = fchmod_opath(fd, minimal & 07777); + if (r < 0) + return r; + } } if (do_chown) if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0) return -errno; - if (do_chmod) - if (fchmod_opath(fd, mode & 07777) < 0) - return -errno; - - return do_chown || do_chmod; -} - -int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid) { - bool do_chown, do_chmod; - struct stat st; - - assert(path); - - /* Change ownership and access mode of the specified path, see description of fchmod_and_chown(). - * Should only be used on trusted paths. */ - - if (lstat(path, &st) < 0) - return -errno; - - do_chown = - (uid != UID_INVALID && st.st_uid != uid) || - (gid != GID_INVALID && st.st_gid != gid); - - do_chmod = - !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */ - ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) || - do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown() - * modifies the access mode too */ - - if (mode == MODE_INVALID) - mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */ - else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0) - return -EINVAL; /* insist on the right file type if it was specified */ - - if (do_chown && do_chmod) { - mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */ - - if (((minimal ^ st.st_mode) & 07777) != 0) - if (chmod(path, minimal & 07777) < 0) - return -errno; + if (do_chmod) { + r = fchmod_opath(fd, mode & 07777); + if (r < 0) + return r; } - if (do_chown) - if (lchown(path, uid, gid) < 0) - return -errno; - - if (do_chmod) - if (chmod(path, mode & 07777) < 0) - return -errno; - return do_chown || do_chmod; } @@ -358,6 +317,25 @@ int fchmod_opath(int fd, mode_t m) { return 0; } +int futimens_opath(int fd, const struct timespec ts[2]) { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + /* Similar to fchmod_path() but for futimens() */ + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } + + return 0; +} + int stat_warn_permissions(const char *path, const struct stat *st) { assert(path); assert(st); @@ -771,7 +749,7 @@ static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), "Detected unsafe path transition %s %s %s during canonicalization of %s.", - n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path); + strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path); } static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { @@ -784,7 +762,7 @@ static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), "Detected autofs mount point %s during canonicalization of %s.", - n1, path); + strna(n1), path); } int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) { @@ -1304,16 +1282,25 @@ int chase_symlinks_and_stat( int access_fd(int fd, int mode) { char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - int r; /* Like access() but operates on an already open fd */ xsprintf(p, "/proc/self/fd/%i", fd); - r = access(p, mode); - if (r < 0) - return -errno; + if (access(p, mode) < 0) { + if (errno != ENOENT) + return -errno; - return r; + /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's + * make things debuggable and distinguish the two. */ + + if (proc_mounted() == 0) + return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot + * environment. */ + + return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ + } + + return 0; } void unlink_tempfilep(char (*p)[]) { @@ -1434,6 +1421,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { return 0; } +#if 0 /* NM_IGNORED */ int fsync_directory_of_file(int fd) { _cleanup_free_ char *path = NULL; _cleanup_close_ int dfd = -1; @@ -1447,9 +1435,9 @@ int fsync_directory_of_file(int fd) { if (r < 0) { log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", fd, - r == -EOPNOTSUPP ? ", ignoring" : ""); + r == -ENOSYS ? ", ignoring" : ""); - if (r == -EOPNOTSUPP) + if (r == -ENOSYS) /* If /proc is not available, we're most likely running in some * chroot environment, and syncing the directory is not very * important in that case. Let's just silently do nothing. */ @@ -1470,6 +1458,7 @@ int fsync_directory_of_file(int fd) { return 0; } +#endif /* NM_IGNORED */ int fsync_full(int fd) { int r, q; @@ -1587,7 +1576,7 @@ static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { d = opendir(p); if (!d) { - if (errno == ENOENT) /* Doesn't have slaves */ + if (errno == ENOENT) /* Doesn't have underlying devices */ return false; return -errno; @@ -1603,7 +1592,7 @@ static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { if (errno != 0) return -errno; - break; /* No more slaves */ + break; /* No more underlying devices */ } q = path_join(p, de->d_name); diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h index b184570f..241cc6ef 100644 --- a/shared/systemd/src/basic/fs-util.h +++ b/shared/systemd/src/basic/fs-util.h @@ -34,11 +34,12 @@ int readlink_and_make_absolute(const char *p, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid); -int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_umask(int fd, mode_t mode); int fchmod_opath(int fd, mode_t m); +int futimens_opath(int fd, const struct timespec ts[2]); + int fd_warn_permissions(const char *path, int fd); int stat_warn_permissions(const char *path, const struct stat *st); @@ -80,7 +81,7 @@ enum { CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */ CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */ CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */ - CHASE_SAFE = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */ + CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */ CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c index c0296bde..b57814e6 100644 --- a/shared/systemd/src/basic/hashmap.c +++ b/shared/systemd/src/basic/hashmap.c @@ -3,6 +3,7 @@ #include "nm-sd-adapt-shared.h" #include <errno.h> +#include <pthread.h> #include <stdint.h> #include <stdlib.h> @@ -21,7 +22,6 @@ #include "strv.h" #if ENABLE_DEBUG_HASHMAP -#include <pthread.h> #include "list.h" #endif @@ -191,7 +191,6 @@ assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); * a handful of directly stored entries in a hashmap. When a hashmap * outgrows direct storage, it gets its own key for indirect storage. */ static uint8_t shared_hash_key[HASH_KEY_SIZE]; -static bool shared_hash_key_initialized; /* Fields that all hashmap/set types must have */ struct HashmapBase { @@ -325,12 +324,12 @@ static void n_entries_dec(HashmapBase *h) { h->n_direct_entries--; } -static void *storage_ptr(HashmapBase *h) { +static void* storage_ptr(HashmapBase *h) { return h->has_indirect ? h->indirect.storage : h->direct.storage; } -static uint8_t *hash_key(HashmapBase *h) { +static uint8_t* hash_key(HashmapBase *h) { return h->has_indirect ? h->indirect.hash_key : shared_hash_key; } @@ -373,16 +372,16 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { memcpy(hash_key, current, sizeof(current)); } -static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { +static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) { return (struct hashmap_base_entry*) ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); } -static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { +static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) { return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); } -static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { +static struct ordered_hashmap_entry* ordered_bucket_at(OrderedHashmap *h, unsigned idx) { return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); } @@ -390,13 +389,13 @@ static struct set_entry *set_bucket_at(Set *h, unsigned idx) { return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); } -static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { +static struct ordered_hashmap_entry* bucket_at_swap(struct swap_entries *swap, unsigned idx) { return &swap->e[idx - _IDX_SWAP_BEGIN]; } /* Returns a pointer to the bucket at index idx. * Understands real indexes and swap indexes, hence "_virtual". */ -static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, +static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, unsigned idx) { if (idx < _IDX_SWAP_BEGIN) return bucket_at(h, idx); @@ -407,7 +406,7 @@ static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_ assert_not_reached("Invalid index"); } -static dib_raw_t *dib_raw_ptr(HashmapBase *h) { +static dib_raw_t* dib_raw_ptr(HashmapBase *h) { return (dib_raw_t*) ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); } @@ -505,7 +504,7 @@ static unsigned prev_idx(HashmapBase *h, unsigned idx) { return (n_buckets(h) + idx - 1U) % n_buckets(h); } -static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { +static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) { switch (h->type) { case HASHMAP_TYPE_PLAIN: @@ -732,16 +731,12 @@ bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **ke return true; } -bool set_iterate(const Set *s, Iterator *i, void **value) { - return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); -} - #define HASHMAP_FOREACH_IDX(idx, h, i) \ for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ (idx != IDX_NIL); \ (idx) = hashmap_iterate_entry((h), &(i))) -IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) { +IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h) { IteratedCache *cache; assert(h); @@ -770,7 +765,11 @@ static void reset_direct_storage(HashmapBase *h) { memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); } -static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { +static void shared_hash_key_initialize(void) { + random_bytes(shared_hash_key, sizeof(shared_hash_key)); +} + +static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *h; const struct hashmap_type_info *hi = &hashmap_type_info[type]; bool up; @@ -792,10 +791,8 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu reset_direct_storage(h); - if (!shared_hash_key_initialized) { - random_bytes(shared_hash_key, sizeof(shared_hash_key)); - shared_hash_key_initialized= true; - } + static pthread_once_t once = PTHREAD_ONCE_INIT; + assert_se(pthread_once(&once, shared_hash_key_initialize) == 0); #if ENABLE_DEBUG_HASHMAP h->debug.func = func; @@ -810,19 +807,19 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu } Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); + return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); + return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); + return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, - enum HashmapType type HASHMAP_DEBUG_PARAMS) { + enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *q; assert(h); @@ -830,7 +827,7 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops if (*h) return 0; - q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); + q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); if (!q) return -ENOMEM; @@ -839,15 +836,25 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops } int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); + return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); +} + +int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) { + int r; + + r = _ordered_hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS); + if (r < 0) + return r; + + return ordered_hashmap_put(*h, key, value); } static void hashmap_free_no_clear(HashmapBase *h) { @@ -868,7 +875,7 @@ static void hashmap_free_no_clear(HashmapBase *h) { free(h); } -HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { if (h) { _hashmap_clear(h, default_free_key, default_free_value); hashmap_free_no_clear(h); @@ -1249,6 +1256,30 @@ int set_put(Set *s, const void *key) { return hashmap_put_boldly(s, hash, &swap, true); } +int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS) { + int r; + + r = _set_ensure_allocated(s, hash_ops HASHMAP_DEBUG_PASS_ARGS); + if (r < 0) + return r; + + return set_put(*s, key); +} + +int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HASHMAP_DEBUG_PARAMS) { + int r; + + r = _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_PASS_ARGS); + if (r <= 0) { + if (hash_ops && hash_ops->free_key) + hash_ops->free_key(key); + else + free(key); + } + + return r; +} + int hashmap_replace(Hashmap *h, const void *key, void *value) { struct swap_entries swap; struct plain_hashmap_entry *e; @@ -1301,7 +1332,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) { return 0; } -void *_hashmap_get(HashmapBase *h, const void *key) { +void* _hashmap_get(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1317,7 +1348,7 @@ void *_hashmap_get(HashmapBase *h, const void *key) { return entry_value(h, e); } -void *hashmap_get2(Hashmap *h, const void *key, void **key2) { +void* hashmap_get2(Hashmap *h, const void *key, void **key2) { struct plain_hashmap_entry *e; unsigned hash, idx; @@ -1346,7 +1377,7 @@ bool _hashmap_contains(HashmapBase *h, const void *key) { return bucket_scan(h, hash, key) != IDX_NIL; } -void *_hashmap_remove(HashmapBase *h, const void *key) { +void* _hashmap_remove(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; void *data; @@ -1366,7 +1397,7 @@ void *_hashmap_remove(HashmapBase *h, const void *key) { return data; } -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { +void* hashmap_remove2(Hashmap *h, const void *key, void **rkey) { struct plain_hashmap_entry *e; unsigned hash, idx; void *data; @@ -1484,7 +1515,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_ return 0; } -void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { +void* _hashmap_remove_value(HashmapBase *h, const void *key, void *value) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1514,7 +1545,7 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { +void* _hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; void *key, *data; unsigned idx; @@ -1689,13 +1720,13 @@ int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { return 0; } -HashmapBase *_hashmap_copy(HashmapBase *h) { +HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) { HashmapBase *copy; int r; assert(h); - copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); + copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_PASS_ARGS); if (!copy) return NULL; @@ -1711,15 +1742,13 @@ HashmapBase *_hashmap_copy(HashmapBase *h) { assert_not_reached("Unknown hashmap type"); } - if (r < 0) { - _hashmap_free(copy, false, false); - return NULL; - } + if (r < 0) + return _hashmap_free(copy, false, false); return copy; } -char **_hashmap_get_strv(HashmapBase *h) { +char** _hashmap_get_strv(HashmapBase *h) { char **sv; Iterator i; unsigned idx, n; @@ -1736,7 +1765,7 @@ char **_hashmap_get_strv(HashmapBase *h) { return sv; } -void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { +void* ordered_hashmap_next(OrderedHashmap *h, const void *key) { struct ordered_hashmap_entry *e; unsigned hash, idx; @@ -1768,10 +1797,10 @@ int set_consume(Set *s, void *value) { } #if 0 /* NM_IGNORED */ -int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) { +int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS) { int r; - r = hashmap_ensure_allocated(h, &string_hash_ops_free_free); + r = _hashmap_ensure_allocated(h, &string_hash_ops_free_free HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; @@ -1803,14 +1832,14 @@ int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) { } #endif /* NM_IGNORED */ -int set_put_strdup(Set **s, const char *p) { +int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS) { char *c; int r; assert(s); assert(p); - r = set_ensure_allocated(s, &string_hash_ops_free); + r = _set_ensure_allocated(s, &string_hash_ops_free HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; @@ -1824,14 +1853,14 @@ int set_put_strdup(Set **s, const char *p) { return set_consume(*s, c); } -int set_put_strdupv(Set **s, char **l) { +int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; char **i; assert(s); STRV_FOREACH(i, l) { - r = set_put_strdup(s, *i); + r = _set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; @@ -1943,7 +1972,7 @@ int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void return 0; } -IteratedCache *iterated_cache_free(IteratedCache *cache) { +IteratedCache* iterated_cache_free(IteratedCache *cache) { if (cache) { free(cache->keys.ptr); free(cache->values.ptr); diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h index 230d3222..890f90a9 100644 --- a/shared/systemd/src/basic/hashmap.h +++ b/shared/systemd/src/basic/hashmap.h @@ -83,8 +83,8 @@ typedef struct { # define HASHMAP_DEBUG_PASS_ARGS #endif -Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +Hashmap* _hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) @@ -96,56 +96,55 @@ OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DE 0; \ }) -HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); -static inline Hashmap *hashmap_free(Hashmap *h) { +HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +static inline Hashmap* hashmap_free(Hashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } -static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { +static inline OrderedHashmap* ordered_hashmap_free(OrderedHashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } -static inline Hashmap *hashmap_free_free(Hashmap *h) { +static inline Hashmap* hashmap_free_free(Hashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } -static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { +static inline OrderedHashmap* ordered_hashmap_free_free(OrderedHashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } -static inline Hashmap *hashmap_free_free_key(Hashmap *h) { +static inline Hashmap* hashmap_free_free_key(Hashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } -static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) { +static inline OrderedHashmap* ordered_hashmap_free_free_key(OrderedHashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } -static inline Hashmap *hashmap_free_free_free(Hashmap *h) { +static inline Hashmap* hashmap_free_free_free(Hashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } -static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { +static inline OrderedHashmap* ordered_hashmap_free_free_free(OrderedHashmap *h) { return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } -IteratedCache *iterated_cache_free(IteratedCache *cache); +IteratedCache* iterated_cache_free(IteratedCache *cache); int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); -HashmapBase *_hashmap_copy(HashmapBase *h); -static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) _hashmap_copy(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h)); -} +HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS); +#define hashmap_copy(h) ((Hashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) +#define ordered_hashmap_copy(h) ((OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h); -static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { +int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS); +#define ordered_hashmap_ensure_put(s, ops, key, value) _ordered_hashmap_ensure_put(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS) + +IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h); +static inline IteratedCache* hashmap_iterated_cache_new(Hashmap *h) { return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } -static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { +static inline IteratedCache* ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } @@ -154,7 +153,8 @@ static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void * return hashmap_put(PLAIN_HASHMAP(h), key, value); } -int hashmap_put_strdup(Hashmap **h, const char *k, const char *v); +int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS); +#define hashmap_put_strdup(h, k, v) _hashmap_put_strdup(h, k, v HASHMAP_DEBUG_SRC_ARGS) int hashmap_update(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { @@ -166,7 +166,7 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo return hashmap_replace(PLAIN_HASHMAP(h), key, value); } -void *_hashmap_get(HashmapBase *h, const void *key); +void* _hashmap_get(HashmapBase *h, const void *key); static inline void *hashmap_get(Hashmap *h, const void *key) { return _hashmap_get(HASHMAP_BASE(h), key); } @@ -174,7 +174,7 @@ static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { return _hashmap_get(HASHMAP_BASE(h), key); } -void *hashmap_get2(Hashmap *h, const void *key, void **rkey); +void* hashmap_get2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); } @@ -187,7 +187,7 @@ static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) return _hashmap_contains(HASHMAP_BASE(h), key); } -void *_hashmap_remove(HashmapBase *h, const void *key); +void* _hashmap_remove(HashmapBase *h, const void *key); static inline void *hashmap_remove(Hashmap *h, const void *key) { return _hashmap_remove(HASHMAP_BASE(h), key); } @@ -195,17 +195,17 @@ static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { return _hashmap_remove(HASHMAP_BASE(h), key); } -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); +void* hashmap_remove2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } -void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value); +void* _hashmap_remove_value(HashmapBase *h, const void *key, void *value); static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { return _hashmap_remove_value(HASHMAP_BASE(h), key, value); } -static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { +static inline void* ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); } @@ -391,13 +391,13 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { }) /* no hashmap_next */ -void *ordered_hashmap_next(OrderedHashmap *h, const void *key); +void* ordered_hashmap_next(OrderedHashmap *h, const void *key); -char **_hashmap_get_strv(HashmapBase *h); -static inline char **hashmap_get_strv(Hashmap *h) { +char** _hashmap_get_strv(HashmapBase *h); +static inline char** hashmap_get_strv(Hashmap *h) { return _hashmap_get_strv(HASHMAP_BASE(h)); } -static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { +static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) { return _hashmap_get_strv(HASHMAP_BASE(h)); } @@ -407,17 +407,25 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { * the entries were inserted. * It is safe to remove the current entry. */ -#define HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define ORDERED_HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) +#define _HASHMAP_FOREACH(e, h, i) \ + for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); ) +#define HASHMAP_FOREACH(e, h) \ + _HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ)) + +#define _ORDERED_HASHMAP_FOREACH(e, h, i) \ + for (Iterator i = ITERATOR_FIRST; ordered_hashmap_iterate((h), &i, (void**)&(e), NULL); ) +#define ORDERED_HASHMAP_FOREACH(e, h) \ + _ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ)) + +#define _HASHMAP_FOREACH_KEY(e, k, h, i) \ + for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); ) +#define HASHMAP_FOREACH_KEY(e, k, h) \ + _HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ)) + +#define _ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ + for (Iterator i = ITERATOR_FIRST; ordered_hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); ) +#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h) \ + _ORDERED_HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ)) DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c index cddc269f..1ea3e7ff 100644 --- a/shared/systemd/src/basic/in-addr-util.c +++ b/shared/systemd/src/basic/in-addr-util.c @@ -16,6 +16,7 @@ #include "macro.h" #include "parse-util.h" #include "random-util.h" +#include "string-util.h" #include "strxcpyx.h" #include "util.h" @@ -55,6 +56,16 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) { + assert(a); + + /* ff02::1 */ + return be32toh(a->s6_addr32[0]) == UINT32_C(0xff020000) && + a->s6_addr32[1] == 0 && + a->s6_addr32[2] == 0 && + be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001); +} + int in_addr_is_multicast(int family, const union in_addr_union *u) { assert(u); @@ -67,6 +78,12 @@ int in_addr_is_multicast(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +bool in4_addr_is_local_multicast(const struct in_addr *a) { + assert(a); + + return (be32toh(a->s_addr) & UINT32_C(0xffffff00)) == UINT32_C(0xe0000000); +} + bool in4_addr_is_localhost(const struct in_addr *a) { assert(a); @@ -109,11 +126,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_ return in4_addr_equal(&a->in, &b->in); if (family == AF_INET6) - return - a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && - a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && - a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && - a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; + return IN6_ARE_ADDR_EQUAL(&a->in6, &b->in6); return -EAFNOSUPPORT; } @@ -411,44 +424,59 @@ int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned } #endif /* NM_IGNORED */ -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { - _cleanup_free_ char *x = NULL; - size_t l; +int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) { + _cleanup_free_ char *ip_str = NULL, *x = NULL; int r; + assert(IN_SET(family, AF_INET, AF_INET6)); assert(u); assert(ret); /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly * handle IPv6 link-local addresses. */ - if (family != AF_INET6) - goto fallback; - if (ifindex <= 0) - goto fallback; - - r = in_addr_is_link_local(family, u); + r = in_addr_to_string(family, u, &ip_str); if (r < 0) return r; - if (r == 0) - goto fallback; - l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; - x = new(char, l); - if (!x) - return -ENOMEM; + if (family == AF_INET6) { + r = in_addr_is_link_local(family, u); + if (r < 0) + return r; + if (r == 0) + ifindex = 0; + } else + ifindex = 0; /* For IPv4 address, ifindex is always ignored. */ - errno = 0; - if (!inet_ntop(family, u, x, l)) - return errno_or_else(EINVAL); + if (port == 0 && ifindex == 0 && isempty(server_name)) { + *ret = TAKE_PTR(ip_str); + return 0; + } - sprintf(strchr(x, 0), "%%%i", ifindex); + const char *separator = isempty(server_name) ? "" : "#"; + server_name = strempty(server_name); + + if (port > 0) { + if (family == AF_INET6) { + if (ifindex > 0) + r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name); + else + r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name); + } else + r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name); + } else { + if (ifindex > 0) + r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name); + else { + x = strjoin(ip_str, separator, server_name); + r = x ? 0 : -ENOMEM; + } + } + if (r < 0) + return -ENOMEM; *ret = TAKE_PTR(x); return 0; - -fallback: - return in_addr_to_string(family, u, ret); } int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { @@ -750,13 +778,13 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func); -static void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) { +void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) { assert(addr); siphash24_compress(addr, sizeof(*addr), state); } -static int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) { +int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) { return memcmp(a, b, sizeof(*a)); } diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h index 90d79a5e..45c93a00 100644 --- a/shared/systemd/src/basic/in-addr-util.h +++ b/shared/systemd/src/basic/in-addr-util.h @@ -12,6 +12,7 @@ union in_addr_union { struct in_addr in; struct in6_addr in6; + uint8_t bytes[CONST_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; }; struct in_addr_data { @@ -26,10 +27,12 @@ int in_addr_is_multicast(int family, const union in_addr_union *u); bool in4_addr_is_link_local(const struct in_addr *a); int in_addr_is_link_local(int family, const union in_addr_union *u); +bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); +bool in4_addr_is_local_multicast(const struct in_addr *a); bool in4_addr_is_non_local(const struct in_addr *a); bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); @@ -40,7 +43,13 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); +int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); +static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { + return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret); +} +static inline int in_addr_port_to_string(int family, const union in_addr_union *u, uint16_t port, char **ret) { + return in_addr_port_ifindex_name_to_string(family, u, port, 0, NULL, ret); +} int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); @@ -73,5 +82,8 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) { * See also oss-fuzz#11344. */ #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} }) +void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state); +int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b); + extern const struct hash_ops in_addr_data_hash_ops; extern const struct hash_ops in6_addr_hash_ops; diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c index f1df3fac..8b0e354a 100644 --- a/shared/systemd/src/basic/io-util.c +++ b/shared/systemd/src/basic/io-util.c @@ -158,10 +158,8 @@ int pipe_eof(int fd) { int r; r = fd_wait_for_event(fd, POLLIN, 0); - if (r < 0) + if (r <= 0) return r; - if (r == 0) - return 0; return !!(r & POLLHUP); } diff --git a/shared/systemd/src/basic/list.h b/shared/systemd/src/basic/list.h index f7f97000..b62c3749 100644 --- a/shared/systemd/src/basic/list.h +++ b/shared/systemd/src/basic/list.h @@ -169,3 +169,18 @@ #define LIST_IS_EMPTY(head) \ (!(head)) + +/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */ +#define LIST_JOIN(name,a,b) \ + do { \ + assert(b); \ + if (!(a)) \ + (a) = (b); \ + else { \ + typeof(*(a)) *_head = (b), *_tail; \ + LIST_FIND_TAIL(name, (a), _tail); \ + _tail->name##_next = _head; \ + _head->name##_prev = _tail; \ + } \ + (b) = NULL; \ + } while (false) diff --git a/shared/systemd/src/basic/log.h b/shared/systemd/src/basic/log.h index 3725b08f..c2ffbb5d 100644 --- a/shared/systemd/src/basic/log.h +++ b/shared/systemd/src/basic/log.h @@ -30,7 +30,7 @@ typedef enum LogTarget{ LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_SYSLOG, LOG_TARGET_SYSLOG_OR_KMSG, - LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ + LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */ LOG_TARGET_NULL, _LOG_TARGET_MAX, _LOG_TARGET_INVALID = -1 @@ -61,10 +61,13 @@ void log_show_location(bool b); bool log_get_show_location(void) _pure_; void log_show_time(bool b); bool log_get_show_time(void) _pure_; +void log_show_tid(bool b); +bool log_get_show_tid(void) _pure_; int log_show_color_from_string(const char *e); int log_show_location_from_string(const char *e); int log_show_time_from_string(const char *e); +int log_show_tid_from_string(const char *e); LogTarget log_get_target(void) _pure_; #if 0 /* NM_IGNORED */ @@ -89,8 +92,11 @@ void log_close(void); void log_forget_fds(void); void log_parse_environment_realm(LogRealm realm); +void log_parse_environment_cli_realm(LogRealm realm); #define log_parse_environment() \ log_parse_environment_realm(LOG_REALM) +#define log_parse_environment_cli() \ + log_parse_environment_cli_realm(LOG_REALM) #if 0 /* NM_IGNORED */ int log_dispatch_internal( @@ -245,12 +251,12 @@ void log_assert_failed_return_realm( #define log_full_errno(level, error, ...) \ log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__) -#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__) +#define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__) int log_emergency_level(void); /* Normal logging */ -#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) +#define log_debug(...) log_full_errno(LOG_DEBUG, 0, __VA_ARGS__) #define log_info(...) log_full(LOG_INFO, __VA_ARGS__) #define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) #define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) @@ -359,3 +365,4 @@ int log_syntax_invalid_utf8_internal( #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) void log_setup_service(void); +void log_setup_cli(void); diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h index 0c2eb894..f872d41f 100644 --- a/shared/systemd/src/basic/macro.h +++ b/shared/systemd/src/basic/macro.h @@ -295,6 +295,15 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ }) +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define CONST_MIN(_A, _B) \ + (__builtin_choose_expr( \ + __builtin_constant_p(_A) && \ + __builtin_constant_p(_B) && \ + __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ + ((_A) < (_B)) ? (_A) : (_B), \ + VOID_0)) + #define MIN3(x, y, z) \ ({ \ const typeof(x) _c = MIN(x, y); \ @@ -538,6 +547,15 @@ static inline int __coverity_check_and_return__(int condition) { (y) = (_t); \ } while (false) +#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) +#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) + +/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */ +#define FOREACH_POINTER(p, x, ...) \ + for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \ + p != (typeof(p)) (void*) -1; \ + p = *(++_l)) + /* Define C11 thread_local attribute even on older gcc compiler * version */ #ifndef thread_local diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h index a6a2ccdb..4f596cff 100644 --- a/shared/systemd/src/basic/memory-util.h +++ b/shared/systemd/src/basic/memory-util.h @@ -13,6 +13,7 @@ size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) #define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1)) +#define PAGE_OFFSET(l) ((l) & (page_size() - 1)) /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ static inline void memcpy_safe(void *dst, const void *src, size_t n) { diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h index 647e56f1..fe4d35bd 100644 --- a/shared/systemd/src/basic/missing_socket.h +++ b/shared/systemd/src/basic/missing_socket.h @@ -65,6 +65,10 @@ struct sockaddr_vm { #define IP_TRANSPARENT 19 #endif +#ifndef IPV6_FREEBIND +#define IPV6_FREEBIND 78 +#endif + /* linux/sockios.h */ #ifndef SIOCGSKNS #define SIOCGSKNS 0x894C diff --git a/shared/systemd/src/basic/missing_stat.h b/shared/systemd/src/basic/missing_stat.h index 7d89a7bb..5d59a214 100644 --- a/shared/systemd/src/basic/missing_stat.h +++ b/shared/systemd/src/basic/missing_stat.h @@ -9,45 +9,129 @@ #include <linux/stat.h> #endif -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */ +#define STATX_DEFINITION { \ + __u32 stx_mask; \ + __u32 stx_blksize; \ + __u64 stx_attributes; \ + __u32 stx_nlink; \ + __u32 stx_uid; \ + __u32 stx_gid; \ + __u16 stx_mode; \ + __u16 __spare0[1]; \ + __u64 stx_ino; \ + __u64 stx_size; \ + __u64 stx_blocks; \ + __u64 stx_attributes_mask; \ + struct statx_timestamp stx_atime; \ + struct statx_timestamp stx_btime; \ + struct statx_timestamp stx_ctime; \ + struct statx_timestamp stx_mtime; \ + __u32 stx_rdev_major; \ + __u32 stx_rdev_minor; \ + __u32 stx_dev_major; \ + __u32 stx_dev_minor; \ + __u64 stx_mnt_id; \ + __u64 __spare2; \ + __u64 __spare3[12]; \ +} + #if !HAVE_STRUCT_STATX struct statx_timestamp { __s64 tv_sec; __u32 tv_nsec; __s32 __reserved; }; -struct statx { - __u32 stx_mask; - __u32 stx_blksize; - __u64 stx_attributes; - __u32 stx_nlink; - __u32 stx_uid; - __u32 stx_gid; - __u16 stx_mode; - __u16 __spare0[1]; - __u64 stx_ino; - __u64 stx_size; - __u64 stx_blocks; - __u64 stx_attributes_mask; - struct statx_timestamp stx_atime; - struct statx_timestamp stx_btime; - struct statx_timestamp stx_ctime; - struct statx_timestamp stx_mtime; - __u32 stx_rdev_major; - __u32 stx_rdev_minor; - __u32 stx_dev_major; - __u32 stx_dev_minor; - __u64 __spare2[14]; -}; + +struct statx STATX_DEFINITION; #endif + +/* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc + * defines an older definition */ +struct new_statx STATX_DEFINITION; #endif /* NM_IGNORED */ /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_BTIME -#define STATX_BTIME 0x00000800U +#ifndef AT_STATX_SYNC_AS_STAT +#define AT_STATX_SYNC_AS_STAT 0x0000 +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef AT_STATX_FORCE_SYNC +#define AT_STATX_FORCE_SYNC 0x2000 #endif /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ #ifndef AT_STATX_DONT_SYNC #define AT_STATX_DONT_SYNC 0x4000 #endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_TYPE +#define STATX_TYPE 0x00000001U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_MODE +#define STATX_MODE 0x00000002U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_NLINK +#define STATX_NLINK 0x00000004U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_UID +#define STATX_UID 0x00000008U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_GID +#define STATX_GID 0x00000010U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_ATIME +#define STATX_ATIME 0x00000020U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_MTIME +#define STATX_MTIME 0x00000040U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_CTIME +#define STATX_CTIME 0x00000080U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_INO +#define STATX_INO 0x00000100U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_SIZE +#define STATX_SIZE 0x00000200U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_BLOCKS +#define STATX_BLOCKS 0x00000400U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_BTIME +#define STATX_BTIME 0x00000800U +#endif + +/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */ +#ifndef STATX_MNT_ID +#define STATX_MNT_ID 0x00001000U +#endif + +/* 80340fe3605c0e78cfe496c3b3878be828cfdbfe (5.8) */ +#ifndef STATX_ATTR_MOUNT_ROOT +#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */ +#endif diff --git a/shared/systemd/src/basic/missing_syscall.h b/shared/systemd/src/basic/missing_syscall.h index 4c42e665..4aed6e7c 100644 --- a/shared/systemd/src/basic/missing_syscall.h +++ b/shared/systemd/src/basic/missing_syscall.h @@ -35,39 +35,49 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old) /* ======================================================================= */ -#if !HAVE_MEMFD_CREATE +#if defined __x86_64__ +# define systemd_NR_memfd_create 319 +#elif defined __arm__ +# define systemd_NR_memfd_create 385 +#elif defined __aarch64__ +# define systemd_NR_memfd_create 279 +#elif defined(__powerpc__) +# define systemd_NR_memfd_create 360 +#elif defined __s390__ +# define systemd_NR_memfd_create 350 +#elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_memfd_create 4354 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_memfd_create 6318 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_memfd_create 5314 +# endif +#elif defined __i386__ +# define systemd_NR_memfd_create 356 +#elif defined __arc__ +# define systemd_NR_memfd_create 279 +#else +# warning "memfd_create() syscall number unknown for your architecture" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_memfd_create && __NR_memfd_create >= 0) -# if defined __NR_memfd_create -# undef __NR_memfd_create -# endif -# if defined __x86_64__ -# define __NR_memfd_create 319 -# elif defined __arm__ -# define __NR_memfd_create 385 -# elif defined __aarch64__ -# define __NR_memfd_create 279 -# elif defined __s390__ -# define __NR_memfd_create 350 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_memfd_create 4354 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_memfd_create 6318 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_memfd_create 5314 -# endif -# elif defined __i386__ -# define __NR_memfd_create 356 -# elif defined __arc__ -# define __NR_memfd_create 279 -# else -# warning "__NR_memfd_create unknown for your architecture" -# endif +#if defined __NR_memfd_create && __NR_memfd_create >= 0 +# if defined systemd_NR_memfd_create +assert_cc(__NR_memfd_create == systemd_NR_memfd_create); +# endif +#else +# if defined __NR_memfd_create +# undef __NR_memfd_create # endif +# if defined systemd_NR_memfd_create +# define __NR_memfd_create systemd_NR_memfd_create +# endif +#endif +#if !HAVE_MEMFD_CREATE static inline int missing_memfd_create(const char *name, unsigned int flags) { # ifdef __NR_memfd_create return syscall(__NR_memfd_create, name, flags); @@ -82,45 +92,53 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) { /* ======================================================================= */ -#if !HAVE_GETRANDOM +#if defined __x86_64__ +# define systemd_NR_getrandom 318 +#elif defined(__i386__) +# define systemd_NR_getrandom 355 +#elif defined(__arm__) +# define systemd_NR_getrandom 384 +#elif defined(__aarch64__) +# define systemd_NR_getrandom 278 +#elif defined(__ia64__) +# define systemd_NR_getrandom 1339 +#elif defined(__m68k__) +# define systemd_NR_getrandom 352 +#elif defined(__s390x__) +# define systemd_NR_getrandom 349 +#elif defined(__powerpc__) +# define systemd_NR_getrandom 359 +#elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_getrandom 4353 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_getrandom 6317 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_getrandom 5313 +# endif +#elif defined(__arc__) +# define systemd_NR_getrandom 278 +#else +# warning "getrandom() syscall number unknown for your architecture" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_getrandom && __NR_getrandom >= 0) -# if defined __NR_getrandom -# undef __NR_getrandom -# endif -# if defined __x86_64__ -# define __NR_getrandom 318 -# elif defined(__i386__) -# define __NR_getrandom 355 -# elif defined(__arm__) -# define __NR_getrandom 384 -# elif defined(__aarch64__) -# define __NR_getrandom 278 -# elif defined(__ia64__) -# define __NR_getrandom 1339 -# elif defined(__m68k__) -# define __NR_getrandom 352 -# elif defined(__s390x__) -# define __NR_getrandom 349 -# elif defined(__powerpc__) -# define __NR_getrandom 359 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_getrandom 4353 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_getrandom 6317 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_getrandom 5313 -# endif -# elif defined(__arc__) -# define __NR_getrandom 278 -# else -# warning "__NR_getrandom unknown for your architecture" -# endif +#if defined __NR_getrandom && __NR_getrandom >= 0 +# if defined systemd_NR_getrandom +assert_cc(__NR_getrandom == systemd_NR_getrandom); +# endif +#else +# if defined __NR_getrandom +# undef __NR_getrandom +# endif +# if defined systemd_NR_getrandom +# define __NR_getrandom systemd_NR_getrandom # endif +#endif +#if !HAVE_GETRANDOM static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) { # ifdef __NR_getrandom return syscall(__NR_getrandom, buffer, count, flags); @@ -135,9 +153,14 @@ static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) /* ======================================================================= */ +/* The syscall has been defined since forever, but the glibc wrapper was missing. */ #if !HAVE_GETTID static inline pid_t missing_gettid(void) { +# if defined __NR_gettid && __NR_gettid >= 0 return (pid_t) syscall(__NR_gettid); +# else +# error "__NR_gettid not defined" +# endif } # define gettid missing_gettid @@ -145,27 +168,39 @@ static inline pid_t missing_gettid(void) { /* ======================================================================= */ -#if !HAVE_NAME_TO_HANDLE_AT +#if defined(__x86_64__) +# define systemd_NR_name_to_handle_at 303 +#elif defined(__i386__) +# define systemd_NR_name_to_handle_at 341 +#elif defined(__arm__) +# define systemd_NR_name_to_handle_at 370 +#elif defined __aarch64__ +# define systemd_NR_name_to_handle_at 264 +#elif defined(__powerpc__) +# define systemd_NR_name_to_handle_at 345 +#elif defined __s390__ || defined __s390x__ +# define systemd_NR_name_to_handle_at 335 +#elif defined(__arc__) +# define systemd_NR_name_to_handle_at 264 +#else +# warning "name_to_handle_at number is not defined" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0) -# if defined __NR_name_to_handle_at -# undef __NR_name_to_handle_at -# endif -# if defined(__x86_64__) -# define __NR_name_to_handle_at 303 -# elif defined(__i386__) -# define __NR_name_to_handle_at 341 -# elif defined(__arm__) -# define __NR_name_to_handle_at 370 -# elif defined(__powerpc__) -# define __NR_name_to_handle_at 345 -# elif defined(__arc__) -# define __NR_name_to_handle_at 264 -# else -# error "__NR_name_to_handle_at is not defined" -# endif +#if defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0 +# if defined systemd_NR_name_to_handle_at +assert_cc(__NR_name_to_handle_at == systemd_NR_name_to_handle_at); # endif +#else +# if defined __NR_name_to_handle_at +# undef __NR_name_to_handle_at +# endif +# if defined systemd_NR_name_to_handle_at +# define __NR_name_to_handle_at systemd_NR_name_to_handle_at +# endif +#endif +#if !HAVE_NAME_TO_HANDLE_AT struct file_handle { unsigned int handle_bytes; int handle_type; @@ -186,23 +221,39 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil /* ======================================================================= */ -#if !HAVE_SETNS +#if defined __aarch64__ +# define systemd_NR_setns 268 +#elif defined __arm__ +# define systemd_NR_setns 375 +#elif defined(__x86_64__) +# define systemd_NR_setns 308 +#elif defined(__i386__) +# define systemd_NR_setns 346 +#elif defined(__powerpc__) +# define systemd_NR_setns 350 +#elif defined __s390__ || defined __s390x__ +# define systemd_NR_setns 339 +#elif defined(__arc__) +# define systemd_NR_setns 268 +#else +# warning "setns() syscall number unknown for your architecture" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_setns && __NR_setns >= 0) -# if defined __NR_setns -# undef __NR_setns -# endif -# if defined(__x86_64__) -# define __NR_setns 308 -# elif defined(__i386__) -# define __NR_setns 346 -# elif defined(__arc__) -# define __NR_setns 268 -# else -# error "__NR_setns is not defined" -# endif +#if defined __NR_setns && __NR_setns >= 0 +# if defined systemd_NR_setns +assert_cc(__NR_setns == systemd_NR_setns); # endif +#else +# if defined __NR_setns +# undef __NR_setns +# endif +# if defined systemd_NR_setns +# define __NR_setns systemd_NR_setns +# endif +#endif +#if !HAVE_SETNS static inline int missing_setns(int fd, int nstype) { # ifdef __NR_setns return syscall(__NR_setns, fd, nstype); @@ -227,41 +278,49 @@ static inline pid_t raw_getpid(void) { /* ======================================================================= */ -#if !HAVE_RENAMEAT2 +#if defined __x86_64__ +# define systemd_NR_renameat2 316 +#elif defined __arm__ +# define systemd_NR_renameat2 382 +#elif defined __aarch64__ +# define systemd_NR_renameat2 276 +#elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_renameat2 4351 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_renameat2 6315 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_renameat2 5311 +# endif +#elif defined __i386__ +# define systemd_NR_renameat2 353 +#elif defined __powerpc64__ +# define systemd_NR_renameat2 357 +#elif defined __s390__ || defined __s390x__ +# define systemd_NR_renameat2 347 +#elif defined __arc__ +# define systemd_NR_renameat2 276 +#else +# warning "renameat2() syscall number unknown for your architecture" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_renameat2 && __NR_renameat2 >= 0) -# if defined __NR_renameat2 -# undef __NR_renameat2 -# endif -# if defined __x86_64__ -# define __NR_renameat2 316 -# elif defined __arm__ -# define __NR_renameat2 382 -# elif defined __aarch64__ -# define __NR_renameat2 276 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_renameat2 4351 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_renameat2 6315 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_renameat2 5311 -# endif -# elif defined __i386__ -# define __NR_renameat2 353 -# elif defined __powerpc64__ -# define __NR_renameat2 357 -# elif defined __s390__ || defined __s390x__ -# define __NR_renameat2 347 -# elif defined __arc__ -# define __NR_renameat2 276 -# else -# warning "__NR_renameat2 unknown for your architecture" -# endif +#if defined __NR_renameat2 && __NR_renameat2 >= 0 +# if defined systemd_NR_renameat2 +assert_cc(__NR_renameat2 == systemd_NR_renameat2); # endif +#else +# if defined __NR_renameat2 +# undef __NR_renameat2 +# endif +# if defined systemd_NR_renameat2 +# define __NR_renameat2 systemd_NR_renameat2 +# endif +#endif +#if !HAVE_RENAMEAT2 static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { # ifdef __NR_renameat2 return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); @@ -328,31 +387,39 @@ static inline key_serial_t missing_request_key(const char *type, const char *des /* ======================================================================= */ -#if !HAVE_COPY_FILE_RANGE +#if defined(__x86_64__) +# define systemd_NR_copy_file_range 326 +#elif defined(__i386__) +# define systemd_NR_copy_file_range 377 +#elif defined __s390__ +# define systemd_NR_copy_file_range 375 +#elif defined __arm__ +# define systemd_NR_copy_file_range 391 +#elif defined __aarch64__ +# define systemd_NR_copy_file_range 285 +#elif defined __powerpc__ +# define systemd_NR_copy_file_range 379 +#elif defined __arc__ +# define systemd_NR_copy_file_range 285 +#else +# warning "copy_file_range() syscall number unknown for your architecture" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_copy_file_range && __NR_copy_file_range >= 0) -# if defined __NR_copy_file_range -# undef __NR_copy_file_range -# endif -# if defined(__x86_64__) -# define __NR_copy_file_range 326 -# elif defined(__i386__) -# define __NR_copy_file_range 377 -# elif defined __s390__ -# define __NR_copy_file_range 375 -# elif defined __arm__ -# define __NR_copy_file_range 391 -# elif defined __aarch64__ -# define __NR_copy_file_range 285 -# elif defined __powerpc__ -# define __NR_copy_file_range 379 -# elif defined __arc__ -# define __NR_copy_file_range 285 -# else -# warning "__NR_copy_file_range not defined for your architecture" -# endif +#if defined __NR_copy_file_range && __NR_copy_file_range >= 0 +# if defined systemd_NR_copy_file_range +assert_cc(__NR_copy_file_range == systemd_NR_copy_file_range); # endif +#else +# if defined __NR_copy_file_range +# undef __NR_copy_file_range +# endif +# if defined systemd_NR_copy_file_range +# define __NR_copy_file_range systemd_NR_copy_file_range +# endif +#endif +#if !HAVE_COPY_FILE_RANGE static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, @@ -370,31 +437,41 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, /* ======================================================================= */ -#if !HAVE_BPF +#if defined __i386__ +# define systemd_NR_bpf 357 +#elif defined __x86_64__ +# define systemd_NR_bpf 321 +#elif defined __aarch64__ +# define systemd_NR_bpf 280 +#elif defined __arm__ +# define systemd_NR_bpf 386 +#elif defined(__powerpc__) +# define systemd_NR_bpf 361 +#elif defined __sparc__ +# define systemd_NR_bpf 349 +#elif defined __s390__ +# define systemd_NR_bpf 351 +#elif defined __tilegx__ +# define systemd_NR_bpf 280 +#else +# warning "bpf() syscall number unknown for your architecture" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_bpf && __NR_bpf >= 0) -# if defined __NR_bpf -# undef __NR_bpf -# endif -# if defined __i386__ -# define __NR_bpf 357 -# elif defined __x86_64__ -# define __NR_bpf 321 -# elif defined __aarch64__ -# define __NR_bpf 280 -# elif defined __arm__ -# define __NR_bpf 386 -# elif defined __sparc__ -# define __NR_bpf 349 -# elif defined __s390__ -# define __NR_bpf 351 -# elif defined __tilegx__ -# define __NR_bpf 280 -# else -# warning "__NR_bpf not defined for your architecture" -# endif +#if defined __NR_bpf && __NR_bpf >= 0 +# if defined systemd_NR_bpf +assert_cc(__NR_bpf == systemd_NR_bpf); +# endif +#else +# if defined __NR_bpf +# undef __NR_bpf # endif +# if defined systemd_NR_bpf +# define __NR_bpf systemd_NR_bpf +# endif +#endif +#if !HAVE_BPF union bpf_attr; static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { @@ -412,69 +489,84 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { /* ======================================================================= */ #ifndef __IGNORE_pkey_mprotect +# if defined __i386__ +# define systemd_NR_pkey_mprotect 380 +# elif defined __x86_64__ +# define systemd_NR_pkey_mprotect 329 +# elif defined __aarch64__ +# define systemd_NR_pkey_mprotect 288 +# elif defined __arm__ +# define systemd_NR_pkey_mprotect 394 +# elif defined __powerpc__ +# define systemd_NR_pkey_mprotect 386 +# elif defined __s390__ +# define systemd_NR_pkey_mprotect 384 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_pkey_mprotect 4363 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_pkey_mprotect 6327 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_pkey_mprotect 5323 +# endif +# else +# warning "pkey_mprotect() syscall number unknown for your architecture" +# endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0) +# if defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0 +# if defined systemd_NR_pkey_mprotect +assert_cc(__NR_pkey_mprotect == systemd_NR_pkey_mprotect); +# endif +# else # if defined __NR_pkey_mprotect # undef __NR_pkey_mprotect # endif -# if defined __i386__ -# define __NR_pkey_mprotect 380 -# elif defined __x86_64__ -# define __NR_pkey_mprotect 329 -# elif defined __arm__ -# define __NR_pkey_mprotect 394 -# elif defined __aarch64__ -# define __NR_pkey_mprotect 394 -# elif defined __powerpc__ -# define __NR_pkey_mprotect 386 -# elif defined __s390__ -# define __NR_pkey_mprotect 384 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_pkey_mprotect 4363 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_pkey_mprotect 6327 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_pkey_mprotect 5323 -# endif -# else -# warning "__NR_pkey_mprotect not defined for your architecture" +# if defined systemd_NR_pkey_mprotect +# define __NR_pkey_mprotect systemd_NR_pkey_mprotect # endif # endif #endif /* ======================================================================= */ -#if !HAVE_STATX +#if defined __aarch64__ +# define systemd_NR_statx 291 +#elif defined __arm__ +# define systemd_NR_statx 397 +#elif defined __alpha__ +# define systemd_NR_statx 522 +#elif defined __i386__ || defined __powerpc64__ +# define systemd_NR_statx 383 +#elif defined __s390__ || defined __s390x__ +# define systemd_NR_statx 379 +#elif defined __sparc__ +# define systemd_NR_statx 360 +#elif defined __x86_64__ +# define systemd_NR_statx 332 +#else +# warning "statx() syscall number unknown for your architecture" +#endif + /* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_statx && __NR_statx >= 0) -# if defined __NR_statx -# undef __NR_statx -# endif -# if defined __aarch64__ || defined __arm__ -# define __NR_statx 397 -# elif defined __alpha__ -# define __NR_statx 522 -# elif defined __i386__ || defined __powerpc64__ -# define __NR_statx 383 -# elif defined __sparc__ -# define __NR_statx 360 -# elif defined __x86_64__ -# define __NR_statx 332 -# else -# warning "__NR_statx not defined for your architecture" -# endif +#if defined __NR_statx && __NR_statx >= 0 +# if defined systemd_NR_statx +assert_cc(__NR_statx == systemd_NR_statx); +# endif +#else +# if defined __NR_statx +# undef __NR_statx +# endif +# if defined systemd_NR_statx +# define __NR_statx systemd_NR_statx # endif - -struct statx; #endif -/* This typedef is supposed to be always defined. */ -typedef struct statx struct_statx; - #if !HAVE_STATX +struct statx; + static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) { # ifdef __NR_statx return syscall(__NR_statx, dfd, filename, flags, mask, buffer); @@ -483,12 +575,18 @@ static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flag return -1; # endif } +#endif -# define statx missing_statx +/* This typedef is supposed to be always defined. */ +typedef struct statx struct_statx; + +#if !HAVE_STATX +# define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer) #endif -#if !HAVE_SET_MEMPOLICY +/* ======================================================================= */ +#if !HAVE_SET_MEMPOLICY enum { MPOL_DEFAULT, MPOL_PREFERRED, @@ -517,7 +615,7 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask, unsigned long maxnode, void *addr, unsigned long flags) { long i; -# ifdef __NR_get_mempolicy +# if defined __NR_get_mempolicy && __NR_get_mempolicy >= 0 i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags); # else errno = ENOSYS; @@ -526,49 +624,87 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask, return i; } -#define get_mempolicy missing_get_mempolicy +# define get_mempolicy missing_get_mempolicy #endif #endif /* NM_IGNORED */ -#if !HAVE_PIDFD_OPEN -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_pidfd_open && __NR_pidfd_open >= 0) -# if defined __NR_pidfd_open -# undef __NR_pidfd_open -# endif -# define __NR_pidfd_open 434 +/* ======================================================================= */ + +/* should be always defined, see kernel 39036cd2727395c3369b1051005da74059a85317 */ +#if defined(__alpha__) +# define systemd_NR_pidfd_send_signal 534 +#else +# define systemd_NR_pidfd_send_signal 424 #endif -static inline int pidfd_open(pid_t pid, unsigned flags) { -#ifdef __NR_pidfd_open - return syscall(__NR_pidfd_open, pid, flags); + +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +#if defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0 +# if defined systemd_NR_pidfd_send_signal +assert_cc(__NR_pidfd_send_signal == systemd_NR_pidfd_send_signal); +# endif #else +# if defined __NR_pidfd_send_signal +# undef __NR_pidfd_send_signal +# endif +# define __NR_pidfd_send_signal systemd_NR_pidfd_send_signal +#endif + +#if !HAVE_PIDFD_SEND_SIGNAL +static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { +# ifdef __NR_pidfd_open + return syscall(__NR_pidfd_send_signal, fd, sig, info, flags); +# else errno = ENOSYS; return -1; -#endif +# endif } + +# define pidfd_send_signal missing_pidfd_send_signal #endif -#if !HAVE_PIDFD_SEND_SIGNAL -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if ! (defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0) -# if defined __NR_pidfd_send_signal -# undef __NR_pidfd_send_signal -# endif -# define __NR_pidfd_send_signal 424 +/* should be always defined, see kernel 7615d9e1780e26e0178c93c55b73309a5dc093d7 */ +#if defined(__alpha__) +# define systemd_NR_pidfd_open 544 +#else +# define systemd_NR_pidfd_open 434 #endif -static inline int pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { -#ifdef __NR_pidfd_open - return syscall(__NR_pidfd_send_signal, fd, sig, info, flags); + +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +#if defined __NR_pidfd_open && __NR_pidfd_open >= 0 +# if defined systemd_NR_pidfd_open +assert_cc(__NR_pidfd_open == systemd_NR_pidfd_open); +# endif #else +# if defined __NR_pidfd_open +# undef __NR_pidfd_open +# endif +# define __NR_pidfd_open systemd_NR_pidfd_open +#endif + +#if !HAVE_PIDFD_OPEN +static inline int missing_pidfd_open(pid_t pid, unsigned flags) { +# ifdef __NR_pidfd_open + return syscall(__NR_pidfd_open, pid, flags); +# else errno = ENOSYS; return -1; -#endif +# endif } + +# define pidfd_open missing_pidfd_open #endif +/* ======================================================================= */ + #if !HAVE_RT_SIGQUEUEINFO -static inline int rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) { +static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) { +# if defined __NR_rt_sigqueueinfo && __NR_rt_sigqueueinfo >= 0 return syscall(__NR_rt_sigqueueinfo, tgid, sig, info); +# else +# error "__NR_rt_sigqueueinfo not defined" +# endif } + +# define rt_sigqueueinfo missing_rt_sigqueueinfo #endif diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c index 0b343b2a..6b47317a 100644 --- a/shared/systemd/src/basic/parse-util.c +++ b/shared/systemd/src/basic/parse-util.c @@ -18,6 +18,9 @@ #include "missing_network.h" #include "parse-util.h" #include "process-util.h" +#if HAVE_SECCOMP +#include "seccomp-util.h" +#endif #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -317,6 +320,7 @@ int parse_errno(const char *t) { return e; } +#if HAVE_SECCOMP int parse_syscall_and_errno(const char *in, char **name, int *error) { _cleanup_free_ char *n = NULL; char *p; @@ -335,7 +339,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) { p = strchr(in, ':'); if (p) { - e = parse_errno(p + 1); + e = seccomp_parse_errno_or_action(p + 1); if (e < 0) return e; @@ -354,6 +358,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) { return 0; } +#endif #endif /* NM_IGNORED */ static const char *mangle_base(const char *s, unsigned *base) { diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h index 9a516ce5..2cee65c4 100644 --- a/shared/systemd/src/basic/parse-util.h +++ b/shared/systemd/src/basic/parse-util.h @@ -19,7 +19,9 @@ int parse_mtu(int family, const char *s, uint32_t *ret); int parse_size(const char *t, uint64_t base, uint64_t *size); int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); +#if HAVE_SECCOMP int parse_syscall_and_errno(const char *in, char **name, int *error); +#endif #define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) #define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c index 45477bc2..644829b2 100644 --- a/shared/systemd/src/basic/path-util.c +++ b/shared/systemd/src/basic/path-util.c @@ -16,6 +16,7 @@ #include "alloc-util.h" #include "extract-word.h" +#include "fd-util.h" #include "fs-util.h" #include "glob-util.h" #include "log.h" @@ -29,15 +30,7 @@ #include "time-util.h" #include "utf8.h" -bool path_is_absolute(const char *p) { - return p[0] == '/'; -} - #if 0 /* NM_IGNORED */ -bool is_path(const char *p) { - return !!strchr(p, '/'); -} - int path_split_and_make_absolute(const char *p, char ***ret) { char **l; int r; @@ -601,9 +594,9 @@ char* path_join_internal(const char *first, ...) { } #if 0 /* NM_IGNORED */ -int find_binary(const char *name, char **ret) { +int find_executable_full(const char *name, bool use_path_envvar, char **ret) { int last_error, r; - const char *p; + const char *p = NULL; assert(name); @@ -620,11 +613,10 @@ int find_binary(const char *name, char **ret) { return 0; } - /** - * Plain getenv, not secure_getenv, because we want - * to actually allow the user to pick the binary. - */ - p = getenv("PATH"); + if (use_path_envvar) + /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the + * binary. */ + p = getenv("PATH"); if (!p) p = DEFAULT_PATH; @@ -647,14 +639,24 @@ int find_binary(const char *name, char **ret) { return -ENOMEM; if (access(j, X_OK) >= 0) { - /* Found it! */ + _cleanup_free_ char *with_dash; - if (ret) { - *ret = path_simplify(j, false); - j = NULL; - } + with_dash = strjoin(j, "/"); + if (!with_dash) + return -ENOMEM; - return 0; + /* If this passes, it must be a directory, and so should be skipped. */ + if (access(with_dash, X_OK) >= 0) + continue; + + /* We can't just `continue` inverting this case, since we need to update last_error. */ + if (errno == ENOTDIR) { + /* Found it! */ + if (ret) + *ret = path_simplify(TAKE_PTR(j), false); + + return 0; + } } /* PATH entries which we don't have access to are ignored, as per tradition. */ @@ -700,18 +702,17 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd return changed; } -static int binary_is_good(const char *binary) { +static int executable_is_good(const char *executable) { _cleanup_free_ char *p = NULL, *d = NULL; int r; - r = find_binary(binary, &p); + r = find_executable(executable, &p); if (r == -ENOENT) return 0; if (r < 0) return r; - /* An fsck that is linked to /bin/true is a non-existent - * fsck */ + /* An fsck that is linked to /bin/true is a non-existent fsck */ r = readlink_malloc(p, &d); if (r == -EINVAL) /* not a symlink */ @@ -734,19 +735,7 @@ int fsck_exists(const char *fstype) { return -EINVAL; checker = strjoina("fsck.", fstype); - return binary_is_good(checker); -} - -int mkfs_exists(const char *fstype) { - const char *mkfs; - - assert(fstype); - - if (streq(fstype, "auto")) - return -EINVAL; - - mkfs = strjoina("mkfs.", fstype); - return binary_is_good(mkfs); + return executable_is_good(checker); } int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) { @@ -1154,4 +1143,10 @@ bool prefixed_path_strv_contains(char **l, const char *path) { return false; } + +bool credential_name_valid(const char *s) { + /* We want that credential names are both valid in filenames (since that's our primary way to pass + * them around) and as fdnames (which is how we might want to pass them around eventually) */ + return filename_is_valid(s) && fdname_is_valid(s); +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h index aba8ad00..6c265519 100644 --- a/shared/systemd/src/basic/path-util.h +++ b/shared/systemd/src/basic/path-util.h @@ -44,9 +44,17 @@ # define DEFAULT_USER_PATH DEFAULT_PATH #endif -bool is_path(const char *p) _pure_; +static inline bool is_path(const char *p) { + assert(p); + return strchr(p, '/'); +} + +static inline bool path_is_absolute(const char *p) { + assert(p); + return p[0] == '/'; +} + int path_split_and_make_absolute(const char *p, char ***ret); -bool path_is_absolute(const char *p) _pure_; char* path_make_absolute(const char *p, const char *prefix); int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); @@ -82,12 +90,14 @@ int path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *root); char** path_strv_resolve_uniq(char **l, const char *root); -int find_binary(const char *name, char **filename); +int find_executable_full(const char *name, bool use_path_envvar, char **ret); +static inline int find_executable(const char *name, char **ret) { + return find_executable_full(name, true, ret); +} bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); int fsck_exists(const char *fstype); -int mkfs_exists(const char *fstype); /* Iterates through the path prefixes of the specified path, going up * the tree, to root. Also returns "" (and not "/"!) for the root @@ -176,3 +186,5 @@ static inline const char *empty_to_root(const char *path) { bool path_strv_contains(char **l, const char *path); bool prefixed_path_strv_contains(char **l, const char *path); + +bool credential_name_valid(const char *s); diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index 6dcb26fb..03ca04e1 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -1344,7 +1344,7 @@ int safe_fork_full( ppid = getppid(); if (ppid == 0) - /* Parent is in a differn't PID namespace. */; + /* Parent is in a different PID namespace. */; else if (ppid != original_pid) { log_debug("Parent died early, raising SIGTERM."); (void) raise(SIGTERM); diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h index 05012377..314a8de1 100644 --- a/shared/systemd/src/basic/process-util.h +++ b/shared/systemd/src/basic/process-util.h @@ -20,14 +20,16 @@ #define procfs_file_alloca(pid, field) \ ({ \ pid_t _pid_ = (pid); \ - const char *_r_; \ + const char *_field_ = (field); \ + char *_r_; \ if (_pid_ == 0) { \ - _r_ = ("/proc/self/" field); \ + _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \ + strcpy(stpcpy(_r_, "/proc/self/"), _field_); \ } else { \ - _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ - sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ + _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \ + sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \ } \ - _r_; \ + (const char*) _r_; \ }) typedef enum ProcessCmdlineFlags { diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c index 512e7af9..9551e762 100644 --- a/shared/systemd/src/basic/random-util.c +++ b/shared/systemd/src/basic/random-util.c @@ -9,11 +9,13 @@ #include <elf.h> #include <errno.h> #include <fcntl.h> +#include <linux/random.h> #include <pthread.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <sys/ioctl.h> #include <sys/time.h> #if HAVE_SYS_AUXV_H @@ -21,6 +23,7 @@ #endif #include "alloc-util.h" +#include "env-util.h" #include "errno-util.h" #include "fd-util.h" #include "fileio.h" @@ -77,7 +80,7 @@ int rdrand(unsigned long *ret) { * hash functions for its hash tables, with a seed generated randomly. The hash tables * systemd employs watch the fill level closely and reseed if necessary. This allows use of * a low quality RNG initially, as long as it improves should a hash table be under attack: - * the attacker after all needs to to trigger many collisions to exploit it for the purpose + * the attacker after all needs to trigger many collisions to exploit it for the purpose * of DoS, but if doing so improves the seed the attack surface is reduced as the attack * takes place. * @@ -116,6 +119,15 @@ int rdrand(unsigned long *ret) { #endif have_rdrand = !!(ecx & bit_RDRND); + + if (have_rdrand > 0) { + /* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0 + If it is unset getenv_bool_secure will return a negative value. */ + if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) { + have_rdrand = false; + return -EOPNOTSUPP; + } + } } if (have_rdrand == 0) @@ -448,4 +460,37 @@ size_t random_pool_size(void) { /* Use the minimum as default, if we can't retrieve the correct value */ return RANDOM_POOL_SIZE_MIN; } + +int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { + int r; + + assert(fd >= 0); + assert(seed && size > 0); + + if (credit) { + _cleanup_free_ struct rand_pool_info *info = NULL; + + /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any + * chance for confusion here. */ + if (size > INT_MAX / 8) + return -EOVERFLOW; + + info = malloc(offsetof(struct rand_pool_info, buf) + size); + if (!info) + return -ENOMEM; + + info->entropy_count = size * 8; + info->buf_size = size; + memcpy(info->buf, seed, size); + + if (ioctl(fd, RNDADDENTROPY, info) < 0) + return -errno; + } else { + r = loop_write(fd, seed, size, false); + if (r < 0) + return r; + } + + return 0; +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h index d8e067d9..7824ffac 100644 --- a/shared/systemd/src/basic/random-util.h +++ b/shared/systemd/src/basic/random-util.h @@ -38,3 +38,5 @@ int rdrand(unsigned long *ret); #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U) size_t random_pool_size(void); + +int random_write_entropy(int fd, const void *seed, size_t size, bool credit); diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h index 621e83bf..7170eea8 100644 --- a/shared/systemd/src/basic/set.h +++ b/shared/systemd/src/basic/set.h @@ -13,22 +13,20 @@ 0; \ }) -Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS) -static inline Set *set_free(Set *s) { +static inline Set* set_free(Set *s) { return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL); } -static inline Set *set_free_free(Set *s) { +static inline Set* set_free_free(Set *s) { return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL); } /* no set_free_free_free */ -static inline Set *set_copy(Set *s) { - return (Set*) _hashmap_copy(HASHMAP_BASE(s)); -} +#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) @@ -36,7 +34,7 @@ int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG int set_put(Set *s, const void *key); /* no set_update */ /* no set_replace */ -static inline void *set_get(const Set *s, void *key) { +static inline void *set_get(const Set *s, const void *key) { return _hashmap_get(HASHMAP_BASE((Set *) s), key); } /* no set_get2 */ @@ -79,7 +77,9 @@ static inline unsigned set_buckets(const Set *s) { return _hashmap_buckets(HASHMAP_BASE((Set *) s)); } -bool set_iterate(const Set *s, Iterator *i, void **value); +static inline bool set_iterate(const Set *s, Iterator *i, void **value) { + return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); +} static inline void set_clear(Set *s) { _hashmap_clear(HASHMAP_BASE(s), NULL, NULL); @@ -120,13 +120,25 @@ static inline char **set_get_strv(Set *s) { return _hashmap_get_strv(HASHMAP_BASE(s)); } +int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS); +#define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) + +int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HASHMAP_DEBUG_PARAMS); +#define set_ensure_consume(s, hash_ops, key) _set_ensure_consume(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) + int set_consume(Set *s, void *value); -int set_put_strdup(Set **s, const char *p); -int set_put_strdupv(Set **s, char **l); + +int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS); +#define set_put_strdup(s, p) _set_put_strdup(s, p HASHMAP_DEBUG_SRC_ARGS) +int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS); +#define set_put_strdupv(s, l) _set_put_strdupv(s, l HASHMAP_DEBUG_SRC_ARGS) + int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); -#define SET_FOREACH(e, s, i) \ - for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) +#define _SET_FOREACH(e, s, i) \ + for (Iterator i = ITERATOR_FIRST; set_iterate((s), &i, (void**)&(e)); ) +#define SET_FOREACH(e, s) \ + _SET_FOREACH(e, s, UNIQ_T(i, UNIQ)) #define SET_FOREACH_MOVE(e, d, s) \ for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) diff --git a/shared/systemd/src/basic/siphash24.h b/shared/systemd/src/basic/siphash24.h index c4e919df..474d9c9d 100644 --- a/shared/systemd/src/basic/siphash24.h +++ b/shared/systemd/src/basic/siphash24.h @@ -6,6 +6,8 @@ #include <string.h> #include <sys/types.h> +#include "time-util.h" + #if 0 /* NM_IGNORED */ struct siphash { uint64_t v0; @@ -17,39 +19,55 @@ struct siphash { }; #else /* NM_IGNORED */ struct siphash { - CSipHash _csiphash; + CSipHash _csiphash; }; static inline void siphash24_init (struct siphash *state, const uint8_t k[16]) { - c_siphash_init ((CSipHash *) state, k); + c_siphash_init ((CSipHash *) state, k); } static inline void siphash24_compress (const void *in, size_t inlen, struct siphash *state) { - c_siphash_append ((CSipHash *) state, in, inlen); + c_siphash_append ((CSipHash *) state, in, inlen); } static inline uint64_t siphash24_finalize (struct siphash *state) { - return c_siphash_finalize ((CSipHash *) state); + return c_siphash_finalize ((CSipHash *) state); } static inline uint64_t siphash24 (const void *in, size_t inlen, const uint8_t k[16]) { - return c_siphash_hash (k, in, inlen); + return c_siphash_hash (k, in, inlen); } #endif /* NM_IGNORED */ void siphash24_init(struct siphash *state, const uint8_t k[static 16]); void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -void siphash24_compress_boolean(bool in, struct siphash *state); #define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) +static inline void siphash24_compress_boolean(bool in, struct siphash *state) { + uint8_t i = in; + + siphash24_compress(&i, sizeof i, state); +} + +static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) { + siphash24_compress(&in, sizeof in, state); +} + +static inline void siphash24_compress_string(const char *in, struct siphash *state) { + if (!in) + return; + + siphash24_compress(in, strlen(in), state); +} + uint64_t siphash24_finalize(struct siphash *state); uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[static 16]); diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c index 9ced3a12..01dce3ab 100644 --- a/shared/systemd/src/basic/socket-util.c +++ b/shared/systemd/src/basic/socket-util.c @@ -30,6 +30,7 @@ #include "macro.h" #include "memory-util.h" #include "missing_socket.h" +#include "missing_network.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -73,7 +74,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) { if (a->sockaddr.in.sin_port == 0) return -EINVAL; - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) + if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -85,7 +86,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) { if (a->sockaddr.in6.sin6_port == 0) return -EINVAL; - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) + if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -110,7 +111,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) { if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) return -EINVAL; } else { - /* If there's no embedded NUL byte, then then the size needs to match the whole + /* If there's no embedded NUL byte, then the size needs to match the whole * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful, * and considers both equivalent: getsockname() even extends sockaddr_un beyond its * size if the path is non NUL terminated.)*/ @@ -119,7 +120,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) { } } - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) + if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) return -EINVAL; return 0; @@ -129,7 +130,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) { if (a->size != sizeof(struct sockaddr_nl)) return -EINVAL; - if (!IN_SET(a->type, SOCK_RAW, SOCK_DGRAM)) + if (!IN_SET(a->type, 0, SOCK_RAW, SOCK_DGRAM)) return -EINVAL; return 0; @@ -138,7 +139,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) { if (a->size != sizeof(struct sockaddr_vm)) return -EINVAL; - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) + if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -404,19 +405,23 @@ int sockaddr_pretty( if (r < 0) return -ENOMEM; } else { - char a[INET6_ADDRSTRLEN]; + char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1]; inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); + if (sa->in6.sin6_scope_id != 0) + format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX); if (include_port) { r = asprintf(&p, - "[%s]:%u", + "[%s]:%u%s%s", a, - be16toh(sa->in6.sin6_port)); + be16toh(sa->in6.sin6_port), + sa->in6.sin6_scope_id != 0 ? "%" : "", + sa->in6.sin6_scope_id != 0 ? ifname : ""); if (r < 0) return -ENOMEM; } else { - p = strdup(a); + p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a); if (!p) return -ENOMEM; } @@ -622,40 +627,64 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b return false; } -int fd_inc_sndbuf(int fd, size_t n) { +int fd_set_sndbuf(int fd, size_t n, bool increase) { int r, value; socklen_t l = sizeof(value); + if (n > INT_MAX) + return -ERANGE; + r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) return 0; - /* If we have the privileges we will ignore the kernel limit. */ + /* First, try to set the buffer size with SO_SNDBUF. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n); + if (r < 0) + return r; - if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n) < 0) { - r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); - if (r < 0) - return r; - } + /* SO_SNDBUF above may set to the kernel limit, instead of the requested size. + * So, we need to check the actual buffer size here. */ + l = sizeof(value); + r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) + return 1; + + /* If we have the privileges we will ignore the kernel limit. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); + if (r < 0) + return r; return 1; } -int fd_inc_rcvbuf(int fd, size_t n) { +int fd_set_rcvbuf(int fd, size_t n, bool increase) { int r, value; socklen_t l = sizeof(value); + if (n > INT_MAX) + return -ERANGE; + r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) return 0; - /* If we have the privileges we will ignore the kernel limit. */ + /* First, try to set the buffer size with SO_RCVBUF. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n); + if (r < 0) + return r; - if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n) < 0) { - r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n); - if (r < 0) - return r; - } + /* SO_RCVBUF above may set to the kernel limit, instead of the requested size. + * So, we need to check the actual buffer size here. */ + l = sizeof(value); + r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) + return 1; + + /* If we have the privileges we will ignore the kernel limit. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n); + if (r < 0) + return r; return 1; } @@ -669,17 +698,19 @@ static const char* const ip_tos_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); -bool ifname_valid_full(const char *p, bool alternative) { +bool ifname_valid_full(const char *p, IfnameValidFlags flags) { bool numeric = true; /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ + assert(!(flags & ~_IFNAME_VALID_ALL)); + if (isempty(p)) return false; - if (alternative) { + if (flags & IFNAME_VALID_ALTERNATIVE) { if (strlen(p) >= ALTIFNAMSIZ) return false; } else { @@ -690,22 +721,27 @@ bool ifname_valid_full(const char *p, bool alternative) { if (dot_or_dot_dot(p)) return false; - while (*p) { - if ((unsigned char) *p >= 127U) + for (const char *t = p; *t; t++) { + if ((unsigned char) *t >= 127U) return false; - if ((unsigned char) *p <= 32U) + if ((unsigned char) *t <= 32U) return false; - if (IN_SET(*p, ':', '/')) + if (IN_SET(*t, ':', '/')) return false; - numeric = numeric && (*p >= '0' && *p <= '9'); - p++; + numeric = numeric && (*t >= '0' && *t <= '9'); } - if (numeric) - return false; + if (numeric) { + if (!(flags & IFNAME_VALID_NUMERIC)) + return false; + + /* Verify that the number is well-formatted and in range. */ + if (parse_ifindex(p) < 0) + return false; + } return true; } @@ -1081,6 +1117,7 @@ int sockaddr_un_unlink(const struct sockaddr_un *sa) { return 1; } +#endif /* NM_IGNORED */ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { size_t l; @@ -1094,12 +1131,10 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { * reference paths in the abstract namespace that include NUL bytes in the name. */ l = strlen(path); - if (l == 0) + if (l < 2) return -EINVAL; if (!IN_SET(path[0], '/', '@')) return -EINVAL; - if (path[1] == 0) - return -EINVAL; /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket @@ -1125,7 +1160,6 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */ } } -#endif /* NM_IGNORED */ int socket_bind_to_ifname(int fd, const char *ifname) { assert(fd >= 0); @@ -1140,6 +1174,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) { int socket_bind_to_ifindex(int fd, int ifindex) { char ifname[IF_NAMESIZE + 1]; + int r; assert(fd >= 0); @@ -1151,10 +1186,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) { return 0; } - if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0) - return 0; - if (errno != ENOPROTOOPT) - return -errno; + r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex); + if (r != -ENOPROTOOPT) + return r; /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */ if (!format_ifname(ifindex, ifname)) @@ -1183,13 +1217,28 @@ ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { return n; } -int socket_pass_pktinfo(int fd, bool b) { +int socket_get_family(int fd, int *ret) { int af; socklen_t sl = sizeof(af); if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) return -errno; + if (sl != sizeof(af)) + return -EINVAL; + + return af; +} + +int socket_set_recvpktinfo(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + switch (af) { case AF_INET: @@ -1205,3 +1254,142 @@ int socket_pass_pktinfo(int fd, bool b) { return -EAFNOSUPPORT; } } + +int socket_set_recverr(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_RECVERR, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVERR, b); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_recvttl(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, b); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_ttl(int fd, int af, int ttl) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_TTL, ttl); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, ttl); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_unicast_if(int fd, int af, int ifi) { + be32_t ifindex_be = htobe32(ifi); + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) + return -errno; + + return 0; + + case AF_INET6: + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) + return -errno; + + return 0; + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_freebind(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_FREEBIND, b); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_transparent(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_TRANSPARENT, b); + + default: + return -EAFNOSUPPORT; + } +} diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h index 5e5cf731..1ece9118 100644 --- a/shared/systemd/src/basic/socket-util.h +++ b/shared/systemd/src/basic/socket-util.h @@ -120,15 +120,26 @@ int netlink_family_from_string(const char *s) _pure_; bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); -int fd_inc_sndbuf(int fd, size_t n); -int fd_inc_rcvbuf(int fd, size_t n); +int fd_set_sndbuf(int fd, size_t n, bool increase); +static inline int fd_inc_sndbuf(int fd, size_t n) { + return fd_set_sndbuf(fd, n, true); +} +int fd_set_rcvbuf(int fd, size_t n, bool increase); +static inline int fd_inc_rcvbuf(int fd, size_t n) { + return fd_set_rcvbuf(fd, n, true); +} int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); -bool ifname_valid_full(const char *p, bool alternative); +typedef enum { + IFNAME_VALID_ALTERNATIVE = 1 << 0, + IFNAME_VALID_NUMERIC = 1 << 1, + _IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC, +} IfnameValidFlags; +bool ifname_valid_full(const char *p, IfnameValidFlags flags); static inline bool ifname_valid(const char *p) { - return ifname_valid_full(p, false); + return ifname_valid_full(p, 0); } bool address_label_valid(const char *p); @@ -207,6 +218,35 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \ }) +#define SOCKADDR_LEN(sa) \ + ({ \ + const union sockaddr_union *__sa = &(sa); \ + size_t _len; \ + switch(__sa->sa.sa_family) { \ + case AF_INET: \ + _len = sizeof(struct sockaddr_in); \ + break; \ + case AF_INET6: \ + _len = sizeof(struct sockaddr_in6); \ + break; \ + case AF_UNIX: \ + _len = SOCKADDR_UN_LEN(__sa->un); \ + break; \ + case AF_PACKET: \ + _len = SOCKADDR_LL_LEN(__sa->ll); \ + break; \ + case AF_NETLINK: \ + _len = sizeof(struct sockaddr_nl); \ + break; \ + case AF_VSOCK: \ + _len = sizeof(struct sockaddr_vm); \ + break; \ + default: \ + assert_not_reached("invalid socket family"); \ + } \ + _len; \ + }) + int socket_ioctl_fd(void); int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path); @@ -223,4 +263,11 @@ int socket_bind_to_ifindex(int fd, int ifindex); ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); -int socket_pass_pktinfo(int fd, bool b); +int socket_get_family(int fd, int *ret); +int socket_set_recvpktinfo(int fd, int af, bool b); +int socket_set_recverr(int fd, int af, bool b); +int socket_set_recvttl(int fd, int af, bool b); +int socket_set_ttl(int fd, int af, int ttl); +int socket_set_unicast_if(int fd, int af, int ifi); +int socket_set_freebind(int fd, int af, bool b); +int socket_set_transparent(int fd, int af, bool b); diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c index 5c51e13a..6d393041 100644 --- a/shared/systemd/src/basic/stat-util.c +++ b/shared/systemd/src/basic/stat-util.c @@ -17,6 +17,7 @@ #include "macro.h" #include "missing_fs.h" #include "missing_magic.h" +#include "missing_syscall.h" #include "parse-util.h" #include "stat-util.h" #include "string-util.h" @@ -420,4 +421,60 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { a->st_ino == b->st_ino && (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ } + +int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) { + static bool avoid_statx = false; + struct stat st; + + if (!avoid_statx) { + if (statx(dfd, path, flags, mask, sx) < 0) { + if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM) + return -errno; + + /* If statx() is not supported or if we see EPERM (which might indicate seccomp + * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back, + * since that is likely an indication of fs access issues, which we should + * propagate */ + } else + return 0; + + avoid_statx = true; + } + + /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are + * OK to ignore */ + if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW| + AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0) + return -EOPNOTSUPP; + + if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0) + return -errno; + + *sx = (struct statx) { + .stx_mask = STATX_TYPE|STATX_MODE| + STATX_NLINK|STATX_UID|STATX_GID| + STATX_ATIME|STATX_MTIME|STATX_CTIME| + STATX_INO|STATX_SIZE|STATX_BLOCKS, + .stx_blksize = st.st_blksize, + .stx_nlink = st.st_nlink, + .stx_uid = st.st_uid, + .stx_gid = st.st_gid, + .stx_mode = st.st_mode, + .stx_ino = st.st_ino, + .stx_size = st.st_size, + .stx_blocks = st.st_blocks, + .stx_rdev_major = major(st.st_rdev), + .stx_rdev_minor = minor(st.st_rdev), + .stx_dev_major = major(st.st_dev), + .stx_dev_minor = minor(st.st_dev), + .stx_atime.tv_sec = st.st_atim.tv_sec, + .stx_atime.tv_nsec = st.st_atim.tv_nsec, + .stx_mtime.tv_sec = st.st_mtim.tv_sec, + .stx_mtime.tv_nsec = st.st_mtim.tv_nsec, + .stx_ctime.tv_sec = st.st_ctim.tv_sec, + .stx_ctime.tv_nsec = st.st_ctim.tv_nsec, + }; + + return 0; +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h index 59aedcb7..26ecd635 100644 --- a/shared/systemd/src/basic/stat-util.h +++ b/shared/systemd/src/basic/stat-util.h @@ -10,6 +10,7 @@ #include <sys/vfs.h> #include "macro.h" +#include "missing_stat.h" int is_symlink(const char *path); int is_dir(const char *path, bool follow); @@ -91,3 +92,24 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret int proc_mounted(void); bool stat_inode_unmodified(const struct stat *a, const struct stat *b); + +int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); + +#if HAS_FEATURE_MEMORY_SANITIZER +# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this." +# define STRUCT_STATX_DEFINE(var) \ + struct statx var = {} +# define STRUCT_NEW_STATX_DEFINE(var) \ + union { \ + struct statx sx; \ + struct new_statx nsx; \ + } var = {} +#else +# define STRUCT_STATX_DEFINE(var) \ + struct statx var +# define STRUCT_NEW_STATX_DEFINE(var) \ + union { \ + struct statx sx; \ + struct new_statx nsx; \ + } var +#endif diff --git a/shared/systemd/src/basic/string-table.c b/shared/systemd/src/basic/string-table.c index 14ae6308..46014b18 100644 --- a/shared/systemd/src/basic/string-table.c +++ b/shared/systemd/src/basic/string-table.c @@ -6,12 +6,10 @@ #include "string-util.h" ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { - size_t i; - if (!key) return -1; - for (i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) if (streq_ptr(table[i], key)) return (ssize_t) i; diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c index c30e88c5..aea13dcb 100644 --- a/shared/systemd/src/basic/string-util.c +++ b/shared/systemd/src/basic/string-util.c @@ -10,12 +10,14 @@ #include "alloc-util.h" #include "escape.h" +#include "extract-word.h" #include "fileio.h" #include "gunicode.h" #include "locale-util.h" #include "macro.h" #include "memory-util.h" #include "string-util.h" +#include "strv.h" #include "terminal-util.h" #include "utf8.h" #include "util.h" @@ -112,83 +114,6 @@ char* first_word(const char *s, const char *word) { return (char*) p; } -static size_t strcspn_escaped(const char *s, const char *reject) { - bool escaped = false; - int n; - - for (n = 0; s[n] != '\0'; n++) { - if (escaped) - escaped = false; - else if (s[n] == '\\') - escaped = true; - else if (strchr(reject, s[n])) - break; - } - - return n; -} - -/* Split a string into words. */ -const char* split( - const char **state, - size_t *l, - const char *separator, - SplitFlags flags) { - - const char *current; - - assert(state); - assert(l); - - if (!separator) - separator = WHITESPACE; - - current = *state; - - if (*current == '\0') /* already at the end? */ - return NULL; - - current += strspn(current, separator); /* skip leading separators */ - if (*current == '\0') { /* at the end now? */ - *state = current; - return NULL; - } - - if (FLAGS_SET(flags, SPLIT_QUOTES)) { - - if (strchr(QUOTES, *current)) { - /* We are looking at a quote */ - *l = strcspn_escaped(current + 1, CHAR_TO_STR(*current)); - if (current[*l + 1] != *current || - (current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end */ - if (FLAGS_SET(flags, SPLIT_RELAX)) { - *state = current + *l + 1 + (current[*l + 1] != '\0'); - return current + 1; - } - *state = current; - return NULL; - } - *state = current++ + *l + 2; - - } else { - /* We are looking at a something that is not a quote */ - *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) { - /* unfinished escape */ - *state = current; - return NULL; - } - *state = current + *l; - } - } else { - *l = strcspn(current, separator); - *state = current + *l; - } - - return current; -} - char *strnappend(const char *s, const char *suffix, size_t b) { size_t a; char *r; @@ -1216,4 +1141,31 @@ int string_extract_line(const char *s, size_t i, char **ret) { 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. */ + const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0; + + const char *found = NULL; + + for (const char *p = string;;) { + _cleanup_free_ char *w = NULL; + int r; + + r = extract_first_word(&p, &w, separators, flags); + if (r < 0) + return r; + if (r == 0) + break; + + found = strv_find(words, w); + if (found) + break; + } + + if (ret_word) + *ret_word = found; + return !!found; +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h index 09131455..cefbda35 100644 --- a/shared/systemd/src/basic/string-util.h +++ b/shared/systemd/src/basic/string-util.h @@ -108,24 +108,6 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_; char *first_word(const char *s, const char *word) _pure_; -typedef enum SplitFlags { - SPLIT_QUOTES = 0x01 << 0, - SPLIT_RELAX = 0x01 << 1, -} SplitFlags; - -/* Smelly. Do not use this anymore. Use extract_first_word() instead! */ -const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); - -/* Similar, don't use this anymore */ -#define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, 0, state) - -#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, 0, state) - -#define _FOREACH_WORD(word, length, s, separator, flags, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags))) - char *strnappend(const char *s, const char *suffix, size_t length); char *strjoin_real(const char *x, ...) _sentinel_; @@ -135,8 +117,8 @@ char *strjoin_real(const char *x, ...) _sentinel_; ({ \ const char *_appendees_[] = { a, __VA_ARGS__ }; \ char *_d_, *_p_; \ - size_t _len_ = 0; \ - size_t _i_; \ + size_t _len_ = 0; \ + size_t _i_; \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ _p_ = _d_ = newa(char, _len_ + 1); \ @@ -152,7 +134,6 @@ char *delete_trailing_chars(char *s, const char *bad); char *truncate_nl(char *s); static inline char *skip_leading_chars(const char *s, const char *bad) { - if (!s) return NULL; @@ -231,11 +212,9 @@ REENABLE_WARNING; /* Like startswith(), but operates on arbitrary memory blocks */ static inline void *memory_startswith(const void *p, size_t sz, const char *token) { - size_t n; - assert(token); - n = strlen(token); + size_t n = strlen(token); if (sz < n) return NULL; @@ -251,20 +230,17 @@ static inline void *memory_startswith(const void *p, size_t sz, const char *toke * It works only for ASCII strings. */ static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) { - size_t n, i; - assert(token); - n = strlen(token); + size_t n = strlen(token); if (sz < n) return NULL; assert(p); - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i])) return NULL; - } return (uint8_t*) p + n; } @@ -286,3 +262,8 @@ 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); +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); +} diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c index 6f812139..30d3af46 100644 --- a/shared/systemd/src/basic/strv.c +++ b/shared/systemd/src/basic/strv.c @@ -260,44 +260,6 @@ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { } #endif /* NM_IGNORED */ -char **strv_split_full(const char *s, const char *separator, SplitFlags flags) { - const char *word, *state; - size_t l; - size_t n, i; - char **r; - - assert(s); - - if (!separator) - separator = WHITESPACE; - - s += strspn(s, separator); - if (isempty(s)) - return new0(char*, 1); - - n = 0; - _FOREACH_WORD(word, l, s, separator, flags, state) - n++; - - r = new(char*, n+1); - if (!r) - return NULL; - - i = 0; - _FOREACH_WORD(word, l, s, separator, flags, state) { - r[i] = strndup(word, l); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - } - - r[i] = NULL; - return r; -} - char **strv_split_newlines(const char *s) { char **l; size_t n; @@ -321,8 +283,7 @@ char **strv_split_newlines(const char *s) { return l; } -#if 0 /* NM_IGNORED */ -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { +int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) { _cleanup_strv_free_ char **l = NULL; size_t n = 0, allocated = 0; int r; @@ -357,9 +318,62 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract return (int) n; } + +#if 0 /* NM_IGNORED */ +int strv_split_colon_pairs(char ***t, const char *s) { + _cleanup_strv_free_ char **l = NULL; + size_t n = 0, allocated = 0; + int r; + + assert(t); + assert(s); + + for (;;) { + _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL; + + r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; + if (r == 0) + break; + + const char *p = tuple; + r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, + &first, &second, NULL); + if (r < 0) + return r; + if (r == 0) + continue; + /* Enforce that at most 2 colon-separated words are contained in each group */ + if (!isempty(p)) + return -EINVAL; + + second_or_empty = strdup(strempty(second)); + if (!second_or_empty) + return -ENOMEM; + + if (!GREEDY_REALLOC(l, allocated, n + 3)) + return -ENOMEM; + + l[n++] = TAKE_PTR(first); + l[n++] = TAKE_PTR(second_or_empty); + + l[n] = NULL; + } + + if (!l) { + l = new0(char*, 1); + if (!l) + return -ENOMEM; + } + + *t = TAKE_PTR(l); + + return (int) n; +} #endif /* NM_IGNORED */ -char *strv_join_prefix(char * const *l, const char *separator, const char *prefix) { +char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { char * const *s; char *r, *e; size_t n, k, m; @@ -370,11 +384,17 @@ char *strv_join_prefix(char * const *l, const char *separator, const char *prefi k = strlen(separator); m = strlen_ptr(prefix); + if (unescape_separators) /* If there separator is multi-char, we won't know how to escape it. */ + assert(k == 1); + n = 0; STRV_FOREACH(s, l) { if (s != l) n += k; - n += m + strlen(*s); + + bool needs_escaping = unescape_separators && strchr(*s, separator[0]); + + n += m + strlen(*s) * (1 + needs_escaping); } r = new(char, n+1); @@ -389,7 +409,16 @@ char *strv_join_prefix(char * const *l, const char *separator, const char *prefi if (prefix) e = stpcpy(e, prefix); - e = stpcpy(e, *s); + bool needs_escaping = unescape_separators && strchr(*s, separator[0]); + + if (needs_escaping) + for (size_t i = 0; (*s)[i]; i++) { + if ((*s)[i] == separator[0]) + *(e++) = '\\'; + *(e++) = (*s)[i]; + } + else + e = stpcpy(e, *s); } *e = 0; diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h index 2ad927bc..919fabf7 100644 --- a/shared/systemd/src/basic/strv.h +++ b/shared/systemd/src/basic/strv.h @@ -72,17 +72,28 @@ static inline bool strv_isempty(char * const *l) { return !l || !*l; } -char **strv_split_full(const char *s, const char *separator, SplitFlags flags); -static inline char **strv_split(const char *s, const char *separator) { - return strv_split_full(s, separator, 0); -} char **strv_split_newlines(const char *s); -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); +int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags); +static inline char **strv_split(const char *s, const char *separators) { + char **ret; + int r; + + r = strv_split_full(&ret, s, separators, 0); + if (r < 0) + return NULL; + + return ret; +} -char *strv_join_prefix(char * const *l, const char *separator, const char *prefix); +/* Given a string containing white-space separated tuples of words themselves separated by ':', + * returns a vector of strings. If the second element in a tuple is missing, the corresponding + * string in the vector is an empty string. */ +int strv_split_colon_pairs(char ***t, const char *s); + +char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); static inline char *strv_join(char * const *l, const char *separator) { - return strv_join_prefix(l, separator, NULL); + return strv_join_full(l, separator, NULL, false); } char **strv_parse_nulstr(const char *s, size_t l); @@ -118,10 +129,6 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; char **strv_sort(char **l); void strv_print(char * const *l); -#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) - -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) - #define strv_from_stdarg_alloca(first) \ ({ \ char **_l; \ diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c index 71af4f21..0fac79a7 100644 --- a/shared/systemd/src/basic/time-util.c +++ b/shared/systemd/src/basic/time-util.c @@ -25,6 +25,7 @@ #include "path-util.h" #include "process-util.h" #include "stat-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "time-util.h" @@ -84,43 +85,82 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { return ts; } +static usec_t map_clock_usec_internal(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: + * + * from - from_base + to_base + * + * But takes care of overflows/underflows and avoids signed operations. */ + + if (from >= from_base) { /* In the future */ + usec_t delta = from - from_base; + + if (to_base >= USEC_INFINITY - delta) /* overflow? */ + return USEC_INFINITY; + + return to_base + delta; + + } else { /* In the past */ + usec_t delta = from_base - from; + + if (to_base <= delta) /* underflow? */ + return 0; + + return to_base - delta; + } +} + +usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) { + + /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock + * onto itself */ + if (map_clock_id(from_clock) == map_clock_id(to_clock)) + return from; + + /* Keep infinity as is */ + if (from == USEC_INFINITY) + return from; + + return map_clock_usec_internal(from, now(from_clock), now(to_clock)); +} + dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { - int64_t delta; assert(ts); - if (u == USEC_INFINITY || u <= 0) { + if (u == USEC_INFINITY || u == 0) { ts->realtime = ts->monotonic = u; return ts; } ts->realtime = u; - - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - + ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC); return ts; } triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { - int64_t delta; + usec_t nowr; assert(ts); - if (u == USEC_INFINITY || u <= 0) { + if (u == USEC_INFINITY || u == 0) { ts->realtime = ts->monotonic = ts->boottime = u; return ts; } + nowr = now(CLOCK_REALTIME); + ts->realtime = u; - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; + ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC)); + ts->boottime = clock_boottime_supported() ? + map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) : + USEC_INFINITY; return ts; } dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; assert(ts); if (u == USEC_INFINITY) { @@ -129,25 +169,28 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { } ts->monotonic = u; - delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta); - + ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME); return ts; } dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; + clockid_t cid; + usec_t nowm; if (u == USEC_INFINITY) { ts->realtime = ts->monotonic = USEC_INFINITY; return ts; } - dual_timestamp_get(ts); - delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub_signed(ts->realtime, delta); - ts->monotonic = usec_sub_signed(ts->monotonic, delta); + cid = clock_boottime_or_monotonic(); + nowm = now(cid); + + if (cid == CLOCK_MONOTONIC) + ts->monotonic = u; + else + ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); + ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME)); return ts; } @@ -203,12 +246,28 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u) { if (u == USEC_INFINITY || u / USEC_PER_SEC >= TIME_T_MAX) { ts->tv_sec = (time_t) -1; - ts->tv_nsec = (long) -1; + ts->tv_nsec = -1L; return ts; } ts->tv_sec = (time_t) (u / USEC_PER_SEC); - ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); + ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC); + + return ts; +} + +struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) { + assert(ts); + + if (n == NSEC_INFINITY || + n / NSEC_PER_SEC >= TIME_T_MAX) { + ts->tv_sec = (time_t) -1; + ts->tv_nsec = -1L; + return ts; + } + + ts->tv_sec = (time_t) (n / NSEC_PER_SEC); + ts->tv_nsec = (long) (n % NSEC_PER_SEC); return ts; } @@ -243,12 +302,11 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) { return tv; } -static char *format_timestamp_internal( +char *format_timestamp_style( char *buf, size_t l, usec_t t, - bool utc, - bool us) { + TimestampStyle style) { /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ @@ -265,9 +323,27 @@ static char *format_timestamp_internal( struct tm tm; time_t sec; size_t n; + bool utc = false, us = false; assert(buf); + switch (style) { + case TIMESTAMP_PRETTY: + break; + case TIMESTAMP_US: + us = true; + break; + case TIMESTAMP_UTC: + utc = true; + break; + case TIMESTAMP_US_UTC: + us = true; + utc = true; + break; + default: + return NULL; + } + if (l < (size_t) (3 + /* week day */ 1 + 10 + /* space and date */ 1 + 8 + /* space and time */ @@ -341,22 +417,6 @@ static char *format_timestamp_internal( return buf; } -char *format_timestamp(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, false); -} - -char *format_timestamp_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, false); -} - -char *format_timestamp_us(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, true); -} - -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, true); -} - char *format_timestamp_relative(char *buf, size_t l, usec_t t) { const char *s; usec_t n, d; @@ -1533,4 +1593,28 @@ int time_change_fd(void) { return -errno; } + +static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = { + [TIMESTAMP_PRETTY] = "pretty", + [TIMESTAMP_US] = "us", + [TIMESTAMP_UTC] = "utc", + [TIMESTAMP_US_UTC] = "us+utc", +}; + +/* Use the macro for enum → string to allow for aliases */ +_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle,); + +/* For the string → enum mapping we use the generic implementation, but also support two aliases */ +TimestampStyle timestamp_style_from_string(const char *s) { + TimestampStyle t; + + t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s); + if (t >= 0) + return t; + if (streq_ptr(s, "µs")) + return TIMESTAMP_US; + if (streq_ptr(s, "µs+uts")) + return TIMESTAMP_US_UTC; + return t; +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/time-util.h b/shared/systemd/src/basic/time-util.h index 4c371257..cecd5efa 100644 --- a/shared/systemd/src/basic/time-util.h +++ b/shared/systemd/src/basic/time-util.h @@ -29,8 +29,17 @@ typedef struct triple_timestamp { usec_t boottime; } triple_timestamp; -#define USEC_INFINITY ((usec_t) -1) -#define NSEC_INFINITY ((nsec_t) -1) +typedef enum TimestampStyle { + TIMESTAMP_PRETTY, + TIMESTAMP_US, + TIMESTAMP_UTC, + TIMESTAMP_US_UTC, + _TIMESTAMP_STYLE_MAX, + _TIMESTAMP_STYLE_INVALID = -1, +} TimestampStyle; + +#define USEC_INFINITY ((usec_t) UINT64_MAX) +#define NSEC_INFINITY ((nsec_t) UINT64_MAX) #define MSEC_PER_SEC 1000ULL #define USEC_PER_SEC ((usec_t) 1000000ULL) @@ -67,6 +76,8 @@ typedef struct triple_timestamp { usec_t now(clockid_t clock); nsec_t now_nsec(clockid_t clock); +usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock); + dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); @@ -101,17 +112,19 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); usec_t timespec_load(const struct timespec *ts) _pure_; nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; struct timespec *timespec_store(struct timespec *ts, usec_t u); +struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n); usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval *timeval_store(struct timeval *tv, usec_t u); -char *format_timestamp(char *buf, size_t l, usec_t t); -char *format_timestamp_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_us(char *buf, size_t l, usec_t t); -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); +char *format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style); char *format_timestamp_relative(char *buf, size_t l, usec_t t); char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); +static inline char *format_timestamp(char *buf, size_t l, usec_t t) { + return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY); +} + int parse_timestamp(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec); @@ -183,3 +196,6 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { #endif int time_change_fd(void); + +const char* timestamp_style_to_string(TimestampStyle t) _const_; +TimestampStyle timestamp_style_from_string(const char *s) _pure_; diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c index 8bbd12b0..9b3621ba 100644 --- a/shared/systemd/src/basic/tmpfile-util.c +++ b/shared/systemd/src/basic/tmpfile-util.c @@ -262,7 +262,7 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { assert((flags & O_EXCL) == 0); /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in - * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in + * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ fd = open_parent(target, O_TMPFILE|flags, 0640); diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c index f2e27b68..8159b187 100644 --- a/shared/systemd/src/basic/utf8.c +++ b/shared/systemd/src/basic/utf8.c @@ -125,7 +125,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { return 0; } -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { +bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) { const char *p; assert(str); @@ -142,7 +142,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { r = utf8_encoded_to_unichar(p, &val); if (r < 0 || unichar_is_control(val) || - (!newline && val == '\n')) + (!allow_newline && val == '\n')) return false; length -= encoded_len; @@ -152,18 +152,22 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { return true; } -char *utf8_is_valid(const char *str) { - const char *p; +char *utf8_is_valid_n(const char *str, size_t len_bytes) { + /* Check if the string is composed of valid utf8 characters. If length len_bytes is given, stop after + * len_bytes. Otherwise, stop at NUL. */ assert(str); - p = str; - while (*p) { + for (const char *p = str; len_bytes != (size_t) -1 ? (size_t) (p - str) < len_bytes : *p != '\0'; ) { int len; - len = utf8_encoded_valid_unichar(p, (size_t) -1); - if (len < 0) - return NULL; + if (_unlikely_(*p == '\0') && len_bytes != (size_t) -1) + return NULL; /* embedded NUL */ + + len = utf8_encoded_valid_unichar(p, + len_bytes != (size_t) -1 ? len_bytes - (p - str) : (size_t) -1); + if (_unlikely_(len < 0)) + return NULL; /* invalid character */ p += len; } diff --git a/shared/systemd/src/basic/utf8.h b/shared/systemd/src/basic/utf8.h index 62e99b72..f315ea0f 100644 --- a/shared/systemd/src/basic/utf8.h +++ b/shared/systemd/src/basic/utf8.h @@ -14,11 +14,14 @@ bool unichar_is_valid(char32_t c); -char *utf8_is_valid(const char *s) _pure_; +char *utf8_is_valid_n(const char *str, size_t len_bytes) _pure_; +static inline char *utf8_is_valid(const char *s) { + return utf8_is_valid_n(s, (size_t) -1); +} char *ascii_is_valid(const char *s) _pure_; char *ascii_is_valid_n(const char *str, size_t len); -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; +bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_; #define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) char *utf8_escape_invalid(const char *s); |