diff options
author | Thomas Haller <thaller@redhat.com> | 2018-12-23 00:46:32 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-12-23 11:42:32 +0100 |
commit | 9e9320cc0f09f1066afe49568c13feda90bcf1be (patch) | |
tree | 26979828f747e03f9e20bc6b116274182e157ad3 | |
parent | bf604ae2d8d57b2087c7358e54a1fe7867d11f11 (diff) | |
parent | c2dbe4b5f77aaed1f00fab69bd5ebaa9cc8fce30 (diff) | |
download | NetworkManager-9e9320cc0f09f1066afe49568c13feda90bcf1be.tar.gz |
systemd: merge branch systemd into master
68 files changed, 2088 insertions, 1505 deletions
diff --git a/Makefile.am b/Makefile.am index 68f4f0a28e..6b0812b6aa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1541,9 +1541,14 @@ src_libsystemd_nm_la_SOURCES = \ src/systemd/sd-adapt/locale-util.h \ src/systemd/sd-adapt/memfd-util.h \ src/systemd/sd-adapt/missing.h \ + src/systemd/sd-adapt/missing_socket.h \ + src/systemd/sd-adapt/missing_syscall.h \ + src/systemd/sd-adapt/missing_timerfd.h \ + src/systemd/sd-adapt/missing_type.h \ src/systemd/sd-adapt/mkdir.h \ src/systemd/sd-adapt/procfs-util.h \ src/systemd/sd-adapt/raw-clone.h \ + src/systemd/sd-adapt/rlimit-util.h \ src/systemd/sd-adapt/sd-daemon.h \ src/systemd/sd-adapt/sd-device.h \ src/systemd/sd-adapt/serialize.h \ @@ -1555,6 +1560,8 @@ src_libsystemd_nm_la_SOURCES = \ src/systemd/src/basic/alloc-util.c \ src/systemd/src/basic/alloc-util.h \ src/systemd/src/basic/async.h \ + src/systemd/src/basic/env-file.c \ + src/systemd/src/basic/env-file.h \ src/systemd/src/basic/env-util.c \ src/systemd/src/basic/env-util.h \ src/systemd/src/basic/escape.c \ @@ -1614,6 +1621,8 @@ src_libsystemd_nm_la_SOURCES = \ src/systemd/src/basic/strv.h \ src/systemd/src/basic/time-util.c \ src/systemd/src/basic/time-util.h \ + src/systemd/src/basic/tmpfile-util.c \ + src/systemd/src/basic/tmpfile-util.h \ src/systemd/src/basic/umask-util.h \ src/systemd/src/basic/utf8.c \ src/systemd/src/basic/utf8.h \ diff --git a/src/systemd/meson.build b/src/systemd/meson.build index ee9912bcfa..adeeb6800b 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -2,6 +2,7 @@ sources = files( 'sd-adapt/nm-sd-adapt.c', 'src/basic/alloc-util.c', 'src/basic/escape.c', + 'src/basic/env-file.c', 'src/basic/env-util.c', 'src/basic/ether-addr-util.c', 'src/basic/extract-word.c', @@ -26,6 +27,7 @@ sources = files( 'src/basic/string-util.c', 'src/basic/strv.c', 'src/basic/time-util.c', + 'src/basic/tmpfile-util.c', 'src/basic/utf8.c', 'src/basic/util.c', 'src/libsystemd-network/arp-util.c', diff --git a/src/systemd/sd-adapt/dirent-util.h b/src/systemd/sd-adapt/dirent-util.h index 637892c2d6..7132dfcc51 100644 --- a/src/systemd/sd-adapt/dirent-util.h +++ b/src/systemd/sd-adapt/dirent-util.h @@ -1,3 +1,5 @@ #pragma once /* dummy header */ + +#include "path-util.h" diff --git a/src/systemd/sd-adapt/missing_socket.h b/src/systemd/sd-adapt/missing_socket.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/src/systemd/sd-adapt/missing_socket.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/missing_syscall.h b/src/systemd/sd-adapt/missing_syscall.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/src/systemd/sd-adapt/missing_syscall.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/missing_timerfd.h b/src/systemd/sd-adapt/missing_timerfd.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/src/systemd/sd-adapt/missing_timerfd.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/missing_type.h b/src/systemd/sd-adapt/missing_type.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/src/systemd/sd-adapt/missing_type.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/rlimit-util.h b/src/systemd/sd-adapt/rlimit-util.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/src/systemd/sd-adapt/rlimit-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c index ef40509833..2e1c7ef61e 100644 --- a/src/systemd/src/basic/alloc-util.c +++ b/src/systemd/src/basic/alloc-util.c @@ -14,7 +14,7 @@ void* memdup(const void *p, size_t l) { assert(l == 0 || p); - ret = malloc(l); + ret = malloc(l ?: 1); if (!ret) return NULL; diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h index 2a6deb12ca..ff7a46793a 100644 --- a/src/systemd/src/basic/alloc-util.h +++ b/src/systemd/src/basic/alloc-util.h @@ -8,9 +8,11 @@ #include "macro.h" +typedef void (*free_func_t)(void *p); + #define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) -#define new0(t, n) ((t*) calloc((n), sizeof(t))) +#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t))) #define newa(t, n) \ ({ \ @@ -75,7 +77,7 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t if (size_multiply_overflow(size, need)) return NULL; - return malloc(size * need); + return malloc(size * need ?: 1); } #if !HAVE_REALLOCARRAY @@ -83,7 +85,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size if (size_multiply_overflow(size, need)) return NULL; - return realloc(p, size * need); + return realloc(p, size * need ?: 1); } #endif diff --git a/src/systemd/src/basic/env-file.c b/src/systemd/src/basic/env-file.c new file mode 100644 index 0000000000..40051c312e --- /dev/null +++ b/src/systemd/src/basic/env-file.c @@ -0,0 +1,578 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "nm-sd-adapt.h" + +#include <stdio_ext.h> + +#include "alloc-util.h" +#include "env-file.h" +#include "env-util.h" +#include "escape.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "string-util.h" +#include "strv.h" +#include "tmpfile-util.h" +#include "utf8.h" + +static int parse_env_file_internal( + FILE *f, + const char *fname, + int (*push) (const char *filename, unsigned line, + const char *key, char *value, void *userdata, int *n_pushed), + void *userdata, + int *n_pushed) { + + size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; + _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; + unsigned line = 1; + char *p; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + VALUE_ESCAPE, + SINGLE_QUOTE_VALUE, + SINGLE_QUOTE_VALUE_ESCAPE, + DOUBLE_QUOTE_VALUE, + DOUBLE_QUOTE_VALUE_ESCAPE, + COMMENT, + COMMENT_ESCAPE + } state = PRE_KEY; + + if (f) + r = read_full_stream(f, &contents, NULL); + else + r = read_full_file(fname, &contents, NULL); + if (r < 0) + return r; + + for (p = contents; *p; p++) { + char c = *p; + + switch (state) { + + case PRE_KEY: + if (strchr(COMMENTS, c)) + state = COMMENT; + else if (!strchr(WHITESPACE, c)) { + state = KEY; + last_key_whitespace = (size_t) -1; + + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + return -ENOMEM; + + key[n_key++] = c; + } + break; + + case KEY: + if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + n_key = 0; + } else if (c == '=') { + state = PRE_VALUE; + last_value_whitespace = (size_t) -1; + } else { + if (!strchr(WHITESPACE, c)) + last_key_whitespace = (size_t) -1; + else if (last_key_whitespace == (size_t) -1) + last_key_whitespace = n_key; + + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + return -ENOMEM; + + key[n_key++] = c; + } + + break; + + case PRE_VALUE: + if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + return r; + + n_key = 0; + value = NULL; + value_alloc = n_value = 0; + + } else if (c == '\'') + state = SINGLE_QUOTE_VALUE; + else if (c == '\"') + state = DOUBLE_QUOTE_VALUE; + else if (c == '\\') + state = VALUE_ESCAPE; + else if (!strchr(WHITESPACE, c)) { + state = VALUE; + + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case VALUE: + if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + /* Chomp off trailing whitespace from value */ + if (last_value_whitespace != (size_t) -1) + value[last_value_whitespace] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + return r; + + n_key = 0; + value = NULL; + value_alloc = n_value = 0; + + } else if (c == '\\') { + state = VALUE_ESCAPE; + last_value_whitespace = (size_t) -1; + } else { + if (!strchr(WHITESPACE, c)) + last_value_whitespace = (size_t) -1; + else if (last_value_whitespace == (size_t) -1) + last_value_whitespace = n_value; + + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case VALUE_ESCAPE: + state = VALUE; + + if (!strchr(NEWLINE, c)) { + /* Escaped newlines we eat up entirely */ + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + break; + + case SINGLE_QUOTE_VALUE: + if (c == '\'') + state = PRE_VALUE; + else if (c == '\\') + state = SINGLE_QUOTE_VALUE_ESCAPE; + else { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case SINGLE_QUOTE_VALUE_ESCAPE: + state = SINGLE_QUOTE_VALUE; + + if (!strchr(NEWLINE, c)) { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + break; + + case DOUBLE_QUOTE_VALUE: + if (c == '\"') + state = PRE_VALUE; + else if (c == '\\') + state = DOUBLE_QUOTE_VALUE_ESCAPE; + else { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case DOUBLE_QUOTE_VALUE_ESCAPE: + state = DOUBLE_QUOTE_VALUE; + + if (!strchr(NEWLINE, c)) { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + break; + + case COMMENT: + if (c == '\\') + state = COMMENT_ESCAPE; + else if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + } + break; + + case COMMENT_ESCAPE: + state = COMMENT; + break; + } + } + + if (IN_SET(state, + PRE_VALUE, + VALUE, + VALUE_ESCAPE, + SINGLE_QUOTE_VALUE, + SINGLE_QUOTE_VALUE_ESCAPE, + DOUBLE_QUOTE_VALUE, + DOUBLE_QUOTE_VALUE_ESCAPE)) { + + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + if (state == VALUE) + if (last_value_whitespace != (size_t) -1) + value[last_value_whitespace] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + return r; + + value = NULL; + } + + return 0; +} + +static int check_utf8ness_and_warn( + const char *filename, unsigned line, + const char *key, char *value) { + + if (!utf8_is_valid(key)) { + _cleanup_free_ char *p = NULL; + + p = utf8_escape_invalid(key); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s:%u: invalid UTF-8 in key '%s', ignoring.", + strna(filename), line, p); + } + + if (value && !utf8_is_valid(value)) { + _cleanup_free_ char *p = NULL; + + p = utf8_escape_invalid(value); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", + strna(filename), line, key, p); + } + + return 0; +} + +static int parse_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + + const char *k; + va_list aq, *ap = userdata; + int r; + + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; + + va_copy(aq, *ap); + + while ((k = va_arg(aq, const char *))) { + char **v; + + v = va_arg(aq, char **); + + if (streq(key, k)) { + va_end(aq); + free(*v); + *v = value; + + if (n_pushed) + (*n_pushed)++; + + return 1; + } + } + + va_end(aq); + free(value); + + return 0; +} + +int parse_env_filev( + FILE *f, + const char *fname, + va_list ap) { + + int r, n_pushed = 0; + va_list aq; + + va_copy(aq, ap); + r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); + va_end(aq); + if (r < 0) + return r; + + return n_pushed; +} + +int parse_env_file_sentinel( + FILE *f, + const char *fname, + ...) { + + va_list ap; + int r; + + va_start(ap, fname); + r = parse_env_filev(f, fname, ap); + va_end(ap); + + return r; +} + +#if 0 /* NM_IGNORED */ +static int load_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + char ***m = userdata; + char *p; + int r; + + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; + + p = strjoin(key, "=", value); + if (!p) + return -ENOMEM; + + r = strv_env_replace(m, p); + if (r < 0) { + free(p); + return r; + } + + if (n_pushed) + (*n_pushed)++; + + free(value); + return 0; +} + +int load_env_file(FILE *f, const char *fname, char ***rl) { + char **m = NULL; + int r; + + r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); + if (r < 0) { + strv_free(m); + return r; + } + + *rl = m; + return 0; +} + +static int load_env_file_push_pairs( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + char ***m = userdata; + int r; + + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; + + r = strv_extend(m, key); + if (r < 0) + return -ENOMEM; + + if (!value) { + r = strv_extend(m, ""); + if (r < 0) + return -ENOMEM; + } else { + r = strv_push(m, value); + if (r < 0) + return r; + } + + if (n_pushed) + (*n_pushed)++; + + return 0; +} + +int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { + char **m = NULL; + int r; + + r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); + if (r < 0) { + strv_free(m); + return r; + } + + *rl = m; + return 0; +} + +static int merge_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + + char ***env = userdata; + char *expanded_value; + + assert(env); + + if (!value) { + log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); + return 0; + } + + if (!env_name_is_valid(key)) { + log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); + free(value); + return 0; + } + + expanded_value = replace_env(value, *env, + REPLACE_ENV_USE_ENVIRONMENT| + REPLACE_ENV_ALLOW_BRACELESS| + REPLACE_ENV_ALLOW_EXTENDED); + if (!expanded_value) + return -ENOMEM; + + free_and_replace(value, expanded_value); + + return load_env_file_push(filename, line, key, value, env, n_pushed); +} + +int merge_env_file( + char ***env, + FILE *f, + const char *fname) { + + /* NOTE: this function supports braceful and braceless variable expansions, + * plus "extended" substitutions, unlike other exported parsing functions. + */ + + return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); +} + +static void write_env_var(FILE *f, const char *v) { + const char *p; + + p = strchr(v, '='); + if (!p) { + /* Fallback */ + fputs_unlocked(v, f); + fputc_unlocked('\n', f); + return; + } + + p++; + fwrite_unlocked(v, 1, p-v, f); + + if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { + fputc_unlocked('\"', f); + + for (; *p; p++) { + if (strchr(SHELL_NEED_ESCAPE, *p)) + fputc_unlocked('\\', f); + + fputc_unlocked(*p, f); + } + + fputc_unlocked('\"', f); + } else + fputs_unlocked(p, f); + + fputc_unlocked('\n', f); +} + +int write_env_file(const char *fname, char **l) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + char **i; + int r; + + assert(fname); + + r = fopen_temporary(fname, &f, &p); + if (r < 0) + return r; + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) fchmod_umask(fileno(f), 0644); + + STRV_FOREACH(i, l) + write_env_var(f, *i); + + r = fflush_and_check(f); + if (r >= 0) { + if (rename(p, fname) >= 0) + return 0; + + r = -errno; + } + + unlink(p); + return r; +} +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/env-file.h b/src/systemd/src/basic/env-file.h new file mode 100644 index 0000000000..e1ca195ff0 --- /dev/null +++ b/src/systemd/src/basic/env-file.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdarg.h> +#include <stdio.h> + +#include "macro.h" + +int parse_env_filev(FILE *f, const char *fname, va_list ap); +int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_; +#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL) +int load_env_file(FILE *f, const char *fname, char ***l); +int load_env_file_pairs(FILE *f, const char *fname, char ***l); + +int merge_env_file(char ***env, FILE *f, const char *fname); + +int write_env_file(const char *fname, char **l); diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h index c612a7c02d..cc6a897c49 100644 --- a/src/systemd/src/basic/escape.h +++ b/src/systemd/src/basic/escape.h @@ -10,7 +10,7 @@ #endif /* NM_IGNORED */ #include "string-util.h" -#include "missing.h" +#include "missing_type.h" /* What characters are special in the shell? */ /* must be escaped outside and inside double-quotes */ diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c index ed92bc609c..602bbae059 100644 --- a/src/systemd/src/basic/ether-addr-util.c +++ b/src/systemd/src/basic/ether-addr-util.c @@ -30,21 +30,15 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR return buffer; } -int ether_addr_compare(const void *a, const void *b) { - assert(a); - assert(b); - +int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) { return memcmp(a, b, ETH_ALEN); } -static void ether_addr_hash_func(const void *p, struct siphash *state) { +static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) { siphash24_compress(p, sizeof(struct ether_addr), state); } -const struct hash_ops ether_addr_hash_ops = { - .hash = ether_addr_hash_func, - .compare = ether_addr_compare -}; +DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare); int ether_addr_from_string(const char *s, struct ether_addr *ret) { size_t pos = 0, n, field; diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h index 3be0370049..4e44b30be9 100644 --- a/src/systemd/src/basic/ether-addr-util.h +++ b/src/systemd/src/basic/ether-addr-util.h @@ -12,7 +12,7 @@ #define ETHER_ADDR_TO_STRING_MAX (3*6) char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); -int ether_addr_compare(const void *a, const void *b); +int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b); static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { return ether_addr_compare(a, b) == 0; } diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index 6c6068038a..a242b739cd 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -25,6 +25,7 @@ #include "socket-util.h" #include "stdio-util.h" #include "util.h" +#include "tmpfile-util.h" int close_nointr(int fd) { assert(fd >= 0); @@ -115,7 +116,7 @@ FILE* safe_fclose(FILE *f) { if (f) { PROTECT_ERRNO; - assert_se(fclose_nointr(f) != EBADF); + assert_se(fclose_nointr(f) != -EBADF); } return NULL; @@ -653,7 +654,7 @@ int fd_duplicate_data_fd(int fd) { if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) { - r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size); + r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL); if (r < 0 && r != -EAGAIN) return r; /* If we get EAGAIN it could be because of the source or because of * the destination fd, we can't know, as sendfile() and friends won't diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index d9794403a1..93b8b2439b 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -2,6 +2,7 @@ #include "nm-sd-adapt.h" +#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <limits.h> @@ -10,32 +11,22 @@ #include <stdio_ext.h> #include <stdlib.h> #include <string.h> -#include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "alloc-util.h" -#include "ctype.h" -#include "env-util.h" -#include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "hexdecoct.h" #include "log.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" -#include "process-util.h" -#include "random-util.h" #include "stdio-util.h" #include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "utf8.h" +#include "tmpfile-util.h" #define READ_FULL_BYTES_MAX (4U*1024U*1024U) @@ -168,7 +159,7 @@ int write_string_file_ts( goto fail; } - f = fdopen(fd, "we"); + f = fdopen(fd, "w"); if (!f) { r = -errno; safe_close(fd); @@ -279,16 +270,20 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { } #endif /* NM_IGNORED */ -int read_full_stream(FILE *f, char **contents, size_t *size) { +int read_full_stream( + FILE *f, + char **ret_contents, + size_t *ret_size) { + _cleanup_free_ char *buf = NULL; struct stat st; size_t n, l; int fd; assert(f); - assert(contents); + assert(ret_contents); - n = LINE_MAX; + n = 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 @@ -344,11 +339,20 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { n = MIN(n * 2, READ_FULL_BYTES_MAX); } + if (!ret_size) { + /* Safety check: if the caller doesn't want to know the size of what we just read it will rely on the + * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise + * there'd be ambiguity about what we just read. */ + + if (memchr(buf, 0, l)) + return -EBADMSG; + } + buf[l] = 0; - *contents = TAKE_PTR(buf); + *ret_contents = TAKE_PTR(buf); - if (size) - *size = l; + if (ret_size) + *ret_size = l; return 0; } @@ -368,566 +372,7 @@ int read_full_file(const char *fn, char **contents, size_t *size) { return read_full_stream(f, contents, size); } -static int parse_env_file_internal( - FILE *f, - const char *fname, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { - - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; - _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; - unsigned line = 1; - char *p; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE, - COMMENT, - COMMENT_ESCAPE - } state = PRE_KEY; - - if (f) - r = read_full_stream(f, &contents, NULL); - else - r = read_full_file(fname, &contents, NULL); - if (r < 0) - return r; - - for (p = contents; *p; p++) { - char c = *p; - - switch (state) { - - case PRE_KEY: - if (strchr(COMMENTS, c)) - state = COMMENT; - else if (!strchr(WHITESPACE, c)) { - state = KEY; - last_key_whitespace = (size_t) -1; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - break; - - case KEY: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - n_key = 0; - } else if (c == '=') { - state = PRE_VALUE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) - last_key_whitespace = n_key; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - - break; - - case PRE_VALUE: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\'') - state = SINGLE_QUOTE_VALUE; - else if (c == '\"') - state = DOUBLE_QUOTE_VALUE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (!strchr(WHITESPACE, c)) { - state = VALUE; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\\') { - state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) - last_value_whitespace = n_value; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE_ESCAPE: - state = VALUE; - - if (!strchr(NEWLINE, c)) { - /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case SINGLE_QUOTE_VALUE: - if (c == '\'') - state = PRE_VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case SINGLE_QUOTE_VALUE_ESCAPE: - state = SINGLE_QUOTE_VALUE; - - if (!strchr(NEWLINE, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case DOUBLE_QUOTE_VALUE: - if (c == '\"') - state = PRE_VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE_ESCAPE: - state = DOUBLE_QUOTE_VALUE; - - if (!strchr(NEWLINE, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case COMMENT: - if (c == '\\') - state = COMMENT_ESCAPE; - else if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - } - break; - - case COMMENT_ESCAPE: - state = COMMENT; - break; - } - } - - if (IN_SET(state, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE)) { - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - if (state == VALUE) - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - value = NULL; - } - - return 0; -} - -static int check_utf8ness_and_warn( - const char *filename, unsigned line, - const char *key, char *value) { - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(key); - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s:%u: invalid UTF-8 in key '%s', ignoring.", - strna(filename), line, p); - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(value); - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", - strna(filename), line, key, p); - } - - return 0; -} - -static int parse_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - va_copy(aq, *ap); - - while ((k = va_arg(aq, const char *))) { - char **v; - - v = va_arg(aq, char **); - - if (streq(key, k)) { - va_end(aq); - free(*v); - *v = value; - - if (n_pushed) - (*n_pushed)++; - - return 1; - } - } - - va_end(aq); - free(value); - - return 0; -} - -int parse_env_filev( - FILE *f, - const char *fname, - va_list ap) { - - int r, n_pushed = 0; - va_list aq; - - va_copy(aq, ap); - r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); - va_end(aq); - if (r < 0) - return r; - - return n_pushed; -} - -int parse_env_file_sentinel( - FILE *f, - const char *fname, - ...) { - - va_list ap; - int r; - - va_start(ap, fname); - r = parse_env_filev(f, fname, ap); - va_end(ap); - - return r; -} - #if 0 /* NM_IGNORED */ -static int load_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - char *p; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - p = strjoin(key, "=", value); - if (!p) - return -ENOMEM; - - r = strv_env_replace(m, p); - if (r < 0) { - free(p); - return r; - } - - if (n_pushed) - (*n_pushed)++; - - free(value); - return 0; -} - -int load_env_file(FILE *f, const char *fname, char ***rl) { - char **m = NULL; - int r; - - r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int load_env_file_push_pairs( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - r = strv_extend(m, key); - if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; -} - -int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { - char **m = NULL; - int r; - - r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int merge_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - char ***env = userdata; - char *expanded_value; - - assert(env); - - if (!value) { - log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); - return 0; - } - - if (!env_name_is_valid(key)) { - log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); - free(value); - return 0; - } - - expanded_value = replace_env(value, *env, - REPLACE_ENV_USE_ENVIRONMENT| - REPLACE_ENV_ALLOW_BRACELESS| - REPLACE_ENV_ALLOW_EXTENDED); - if (!expanded_value) - return -ENOMEM; - - free_and_replace(value, expanded_value); - - return load_env_file_push(filename, line, key, value, env, n_pushed); -} - -int merge_env_file( - char ***env, - FILE *f, - const char *fname) { - - /* NOTE: this function supports braceful and braceless variable expansions, - * plus "extended" substitutions, unlike other exported parsing functions. - */ - - return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); -} - -static void write_env_var(FILE *f, const char *v) { - const char *p; - - p = strchr(v, '='); - if (!p) { - /* Fallback */ - fputs_unlocked(v, f); - fputc_unlocked('\n', f); - return; - } - - p++; - fwrite_unlocked(v, 1, p-v, f); - - if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc_unlocked('\"', f); - - for (; *p; p++) { - if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc_unlocked('\\', f); - - fputc_unlocked(*p, f); - } - - fputc_unlocked('\"', f); - } else - fputs_unlocked(p, f); - - fputc_unlocked('\n', f); -} - -int write_env_file(const char *fname, char **l) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - char **i; - int r; - - assert(fname); - - r = fopen_temporary(fname, &f, &p); - if (r < 0) - return r; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - (void) fchmod_umask(fileno(f), 0644); - - STRV_FOREACH(i, l) - write_env_var(f, *i); - - r = fflush_and_check(f); - if (r >= 0) { - if (rename(p, fname) >= 0) - return 0; - - r = -errno; - } - - unlink(p); - return r; -} - int executable_is_script(const char *path, char **interpreter) { _cleanup_free_ char *line = NULL; size_t len; @@ -1136,39 +581,6 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root } #endif /* NM_IGNORED */ -int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { - FILE *f; - char *t; - int r, fd; - - assert(path); - assert(_f); - assert(_temp_path); - - r = tempfn_xxxxxx(path, NULL, &t); - if (r < 0) - return r; - - fd = mkostemp_safe(t); - if (fd < 0) { - free(t); - return -errno; - } - - f = fdopen(fd, "we"); - if (!f) { - unlink_noerrno(t); - free(t); - safe_close(fd); - return -errno; - } - - *_f = f; - *_temp_path = t; - - return 0; -} - int fflush_and_check(FILE *f) { assert(f); @@ -1200,165 +612,6 @@ int fflush_sync_and_check(FILE *f) { return 0; } -#endif /* NM_IGNORED */ - -/* This is much like mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern) { - _cleanup_umask_ mode_t u = 0; - int fd; - - assert(pattern); - - u = umask(077); - - fd = mkostemp(pattern, O_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; -} - -int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { - int fd; - FILE *f; - - fd = mkostemp_safe(pattern); - if (fd < 0) - return fd; - - f = fdopen(fd, mode); - if (!f) { - safe_close(fd); - return -errno; - } - - *ret_f = f; - return 0; -} - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - const char *fn; - char *t; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldoXXXXXX - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); - if (!t) - return -ENOMEM; - - strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); - - *ret = path_simplify(t, false); - return 0; -} - -#if 0 /* NM_IGNORED */ -int tempfn_random(const char *p, const char *extra, char **ret) { - const char *fn; - char *t, *x; - uint64_t u; - unsigned i; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldobaa2a261115984a9 - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} - -int tempfn_random_child(const char *p, const char *extra, char **ret) { - char *t, *x; - uint64_t u; - unsigned i; - int r; - - assert(ret); - - /* Turns this: - * /foo/bar/waldo - * Into this: - * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 - */ - - if (!p) { - r = tmp_dir(&p); - if (r < 0) - return r; - } - - extra = strempty(extra); - - t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - if (isempty(p)) - x = stpcpy(stpcpy(t, ".#"), extra); - else - x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} int write_timestamp_file_atomic(const char *fn, usec_t n) { char ln[DECIMAL_STR_MAX(n)+2]; @@ -1425,198 +678,53 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) } #if 0 /* NM_IGNORED */ -int open_tmpfile_unlinkable(const char *directory, int flags) { - char *p; - int fd, r; - - if (!directory) { - r = tmp_dir(&directory); - if (r < 0) - return r; - } else if (isempty(directory)) - return -EINVAL; - - /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ - - /* Try O_TMPFILE first, if it is supported */ - fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; - - /* Fall back to unguessable name + unlinking */ - p = strjoina(directory, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p); - if (fd < 0) - return fd; - - (void) unlink(p); - - return fd; -} - -int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { - _cleanup_free_ char *tmp = NULL; - int r, fd; - - assert(target); - assert(ret_path); - - /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ - 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 - * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ - - fd = open_parent(target, O_TMPFILE|flags, 0640); - if (fd >= 0) { - *ret_path = NULL; - return fd; - } - - log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target); - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; - - fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); - if (fd < 0) - return -errno; - - *ret_path = TAKE_PTR(tmp); - - return fd; -} - -int open_serialization_fd(const char *ident) { - int fd = -1; - - fd = memfd_create(ident, MFD_CLOEXEC); - if (fd < 0) { - const char *path; - - path = getpid_cached() == 1 ? "/run/systemd" : "/tmp"; - fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); - if (fd < 0) - return fd; - - log_debug("Serializing %s to %s.", ident, path); - } else - log_debug("Serializing %s to memfd.", ident); - - return fd; -} - -int link_tmpfile(int fd, const char *path, const char *target) { - int r; - - assert(fd >= 0); - assert(target); - - /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd - * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported - * on the directory, and renameat2() is used instead. - * - * Note that in both cases we will not replace existing files. This is because linkat() does not support this - * operation currently (renameat2() does), and there is no nice way to emulate this. */ - - if (path) { - r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target); - if (r < 0) - return r; - } else { - char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return -errno; - } - - return 0; -} - -int read_nul_string(FILE *f, char **ret) { - _cleanup_free_ char *x = NULL; - size_t allocated = 0, n = 0; - - assert(f); - assert(ret); - - /* Reads a NUL-terminated string from the specified file. */ - - for (;;) { - int c; - - if (!GREEDY_REALLOC(x, allocated, n+2)) - return -ENOMEM; - - c = fgetc(f); - if (c == 0) /* Terminate at NUL byte */ - break; - if (c == EOF) { - if (ferror(f)) - return -errno; - break; /* Terminate at EOF */ - } +/* A bitmask of the EOL markers we know */ +typedef enum EndOfLineMarker { + EOL_NONE = 0, + EOL_ZERO = 1 << 0, /* \0 (aka NUL) */ + EOL_TEN = 1 << 1, /* \n (aka NL, aka LF) */ + EOL_THIRTEEN = 1 << 2, /* \r (aka CR) */ +} EndOfLineMarker; - x[n++] = (char) c; - } +static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) { - if (x) - x[n] = 0; - else { - x = new0(char, 1); - if (!x) - return -ENOMEM; + if (!IN_SET(flags, READ_LINE_ONLY_NUL)) { + if (c == '\n') + return EOL_TEN; + if (c == '\r') + return EOL_THIRTEEN; } - *ret = TAKE_PTR(x); - - return 0; -} + if (c == '\0') + return EOL_ZERO; -int mkdtemp_malloc(const char *template, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - assert(ret); - - if (template) - p = strdup(template); - else { - const char *tmp; - - r = tmp_dir(&tmp); - if (r < 0) - return r; - - p = strjoin(tmp, "/XXXXXX"); - } - if (!p) - return -ENOMEM; - - if (!mkdtemp(p)) - return -errno; - - *ret = TAKE_PTR(p); - return 0; + return EOL_NONE; } DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); -int read_line(FILE *f, size_t limit, char **ret) { - _cleanup_free_ char *buffer = NULL; +int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { size_t n = 0, allocated = 0, count = 0; + _cleanup_free_ char *buffer = NULL; + int r; assert(f); /* Something like a bounded version of getline(). * - * Considers EOF, \n and \0 end of line delimiters, and does not include these delimiters in the string - * returned. + * Considers EOF, \n, \r and \0 end of line delimiters (or combinations of these), and does not include these + * delimiters in the string returned. Specifically, recognizes the following combinations of markers as line + * endings: + * + * • \n (UNIX) + * • \r (old MacOS) + * • \0 (C strings) + * • \n\0 + * • \r\0 + * • \r\n (Windows) + * • \n\r + * • \r\n\0 + * • \n\r\0 * * Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from * the number of characters in the returned string). When EOF is hit, 0 is returned. @@ -1633,34 +741,55 @@ int read_line(FILE *f, size_t limit, char **ret) { { _unused_ _cleanup_(funlockfilep) FILE *flocked = f; + EndOfLineMarker previous_eol = EOL_NONE; flockfile(f); for (;;) { - int c; + EndOfLineMarker eol; + char c; if (n >= limit) return -ENOBUFS; - errno = 0; - c = fgetc_unlocked(f); - if (c == EOF) { - /* if we read an error, and have no data to return, then propagate the error */ - if (ferror_unlocked(f) && n == 0) - return errno > 0 ? -errno : -EIO; + if (count >= INT_MAX) /* We couldn't return the counter anymore as "int", hence refuse this */ + return -ENOBUFS; + + r = safe_fgetc(f, &c); + if (r < 0) + return r; + if (r == 0) /* EOF is definitely EOL */ + break; + eol = categorize_eol(c, flags); + + if (FLAGS_SET(previous_eol, EOL_ZERO) || + (eol == EOL_NONE && previous_eol != EOL_NONE) || + (eol != EOL_NONE && (previous_eol & eol) != 0)) { + /* Previous char was a NUL? This is not an EOL, but the previous char was? This type of + * EOL marker has been seen right before? In either of these three cases we are + * done. But first, let's put this character back in the queue. (Note that we have to + * cast this to (unsigned char) here as ungetc() expects a positive 'int', and if we + * are on an architecture where 'char' equals 'signed char' we need to ensure we don't + * pass a negative value here. That said, to complicate things further ungetc() is + * actually happy with most negative characters and implicitly casts them back to + * positive ones as needed, except for \xff (aka -1, aka EOF), which it refuses. What a + * godawful API!) */ + assert_se(ungetc((unsigned char) c, f) != EOF); break; } count++; - if (IN_SET(c, '\n', 0)) /* Reached a delimiter */ - break; + if (eol != EOL_NONE) { + previous_eol |= eol; + continue; + } if (ret) { if (!GREEDY_REALLOC(buffer, allocated, n + 2)) return -ENOMEM; - buffer[n] = (char) c; + buffer[n] = c; } n++; @@ -1675,4 +804,31 @@ int read_line(FILE *f, size_t limit, char **ret) { return (int) count; } + +int safe_fgetc(FILE *f, char *ret) { + int k; + + assert(f); + + /* A safer version of plain fgetc(): let's propagate the error that happened while reading as such, and + * separate the EOF condition from the byte read, to avoid those confusion signed/unsigned issues fgetc() + * has. */ + + errno = 0; + k = fgetc(f); + if (k == EOF) { + if (ferror(f)) + return errno > 0 ? -errno : -EIO; + + if (ret) + *ret = 0; + + return 0; + } + + if (ret) + *ret = k; + + return 1; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index dae115e3bb..53e3f4ef5f 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -44,16 +44,6 @@ int read_full_stream(FILE *f, char **contents, size_t *size); int verify_file(const char *fn, const char *blob, bool accept_extra_nl); -int parse_env_filev(FILE *f, const char *fname, va_list ap); -int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_; -#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL) -int load_env_file(FILE *f, const char *fname, char ***l); -int load_env_file_pairs(FILE *f, const char *fname, char ***l); - -int merge_env_file(char ***env, FILE *f, const char *fname); - -int write_env_file(const char *fname, char **l); - int executable_is_script(const char *path, char **interpreter); int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); @@ -66,27 +56,23 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root int fflush_and_check(FILE *f); int fflush_sync_and_check(FILE *f); -int fopen_temporary(const char *path, FILE **_f, char **_temp_path); -int mkostemp_safe(char *pattern); -int fmkostemp_safe(char *pattern, const char *mode, FILE**_f); - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret); -int tempfn_random(const char *p, const char *extra, char **ret); -int tempfn_random_child(const char *p, const char *extra, char **ret); - int write_timestamp_file_atomic(const char *fn, usec_t n); int read_timestamp_file(const char *fn, usec_t *ret); int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); -int open_tmpfile_unlinkable(const char *directory, int flags); -int open_tmpfile_linkable(const char *target, int flags, char **ret_path); -int open_serialization_fd(const char *ident); +typedef enum ReadLineFlags { + READ_LINE_ONLY_NUL = 1 << 0, +} ReadLineFlags; -int link_tmpfile(int fd, const char *path, const char *target); +int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret); -int read_nul_string(FILE *f, char **ret); +static inline int read_line(FILE *f, size_t limit, char **ret) { + return read_line_full(f, limit, 0, ret); +} -int mkdtemp_malloc(const char *template, char **ret); +static inline int read_nul_string(FILE *f, size_t limit, char **ret) { + return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret); +} -int read_line(FILE *f, size_t limit, char **ret); +int safe_fgetc(FILE *f, char *ret); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c index 5966c3835b..767e5e078f 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -15,8 +15,8 @@ #include "alloc-util.h" #include "dirent-util.h" #include "fd-util.h" -#include "fileio.h" #include "fs-util.h" +#include "locale-util.h" #include "log.h" #include "macro.h" #include "missing.h" @@ -29,6 +29,7 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "tmpfile-util.h" #include "user-util.h" #include "util.h" @@ -216,31 +217,62 @@ 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) { + char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + _cleanup_close_ int fd = -1; assert(path); - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out + /* Under the assumption that we are running privileged we first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ - if (mode != MODE_INVALID) - if (chmod(path, mode) < 0) + fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change mode/owner + * on the same file */ + if (fd < 0) + return -errno; + + xsprintf(fd_path, "/proc/self/fd/%i", fd); + + if (mode != MODE_INVALID) { + + if ((mode & S_IFMT) != 0) { + struct stat st; + + if (stat(fd_path, &st) < 0) + return -errno; + + if ((mode & S_IFMT) != (st.st_mode & S_IFMT)) + return -EINVAL; + } + + if (chmod(fd_path, mode & 07777) < 0) return -errno; + } if (uid != UID_INVALID || gid != GID_INVALID) - if (chown(path, uid, gid) < 0) + if (chown(fd_path, uid, gid) < 0) return -errno; return 0; } int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out + /* Under the assumption that we are running privileged we first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ - if (mode != MODE_INVALID) - if (fchmod(fd, mode) < 0) + if (mode != MODE_INVALID) { + + if ((mode & S_IFMT) != 0) { + struct stat st; + + if (fstat(fd, &st) < 0) + return -errno; + + if ((mode & S_IFMT) != (st.st_mode & S_IFMT)) + return -EINVAL; + } + + if (fchmod(fd, mode & 0777) < 0) return -errno; + } if (uid != UID_INVALID || gid != GID_INVALID) if (fchown(fd, uid, gid) < 0) @@ -270,7 +302,6 @@ int fchmod_opath(int fd, mode_t m) { * fchownat() does. */ xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (chmod(procfs_path, m) < 0) return -errno; @@ -642,15 +673,42 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { } #if 0 /* NM_IGNORED */ -static bool safe_transition(const struct stat *a, const struct stat *b) { +static bool unsafe_transition(const struct stat *a, const struct stat *b) { /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ - return true; + return false; + + return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */ +} + +static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { + _cleanup_free_ char *n1 = NULL, *n2 = NULL; - return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */ + if (!FLAGS_SET(flags, CHASE_WARN)) + return -ENOLINK; + + (void) fd_get_path(a, &n1); + (void) fd_get_path(b, &n2); + + 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); +} + +static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { + _cleanup_free_ char *n1 = NULL; + + if (!FLAGS_SET(flags, CHASE_WARN)) + return -EREMOTE; + + (void) fd_get_path(fd, &n1); + + return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), + "Detected autofs mount point %s during canonicalization of %s.", + n1, path); } int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) { @@ -713,6 +771,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * path is fully normalized, and == 0 for each normalization step. This may be combined with * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. * + * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from + * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If + * CHASE_WARN is also set a warning describing the unsafe transition is emitted. + * + * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, the path normalization is + * aborted and -EREMOTE is returned. If CHASE_WARN is also set a warning showing the path of the mount point + * is emitted. + * * */ /* A root directory of "/" or "" is identical to none */ @@ -827,8 +893,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(fd_parent, &st) < 0) return -errno; - if (!safe_transition(&previous_stat, &st)) - return -EPERM; + if (unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(fd, fd_parent, path, flags); previous_stat = st; } @@ -868,14 +934,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; if ((flags & CHASE_SAFE) && - !safe_transition(&previous_stat, &st)) - return -EPERM; + unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(fd, child, path, flags); previous_stat = st; if ((flags & CHASE_NO_AUTOFS) && fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) - return -EREMOTE; + return log_autofs_mount_point(child, path, flags); if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { char *joined; @@ -907,8 +973,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(fd, &st) < 0) return -errno; - if (!safe_transition(&previous_stat, &st)) - return -EPERM; + if (unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(child, fd, path, flags); previous_stat = st; } diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h index 955b146a6a..7ad030be5d 100644 --- a/src/systemd/src/basic/fs-util.h +++ b/src/systemd/src/basic/fs-util.h @@ -74,6 +74,7 @@ enum { CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */ CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */ CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */ + CHASE_WARN = 1 << 8, /* Emit an appropriate warning when an error is encountered */ }; /* How many iterations to execute before returning -ELOOP */ diff --git a/src/systemd/src/basic/hash-funcs.c b/src/systemd/src/basic/hash-funcs.c index fc2c4f7824..33264859ed 100644 --- a/src/systemd/src/basic/hash-funcs.c +++ b/src/systemd/src/basic/hash-funcs.c @@ -7,22 +7,14 @@ #include "hash-funcs.h" #include "path-util.h" -void string_hash_func(const void *p, struct siphash *state) { +void string_hash_func(const char *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); } #if 0 /* NM_IGNORED */ -int string_compare_func(const void *a, const void *b) { - return strcmp(a, b); -} - -const struct hash_ops string_hash_ops = { - .hash = string_hash_func, - .compare = string_compare_func -}; +DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func); -void path_hash_func(const void *p, struct siphash *state) { - const char *q = p; +void path_hash_func(const char *q, struct siphash *state) { size_t n; assert(q); @@ -60,14 +52,11 @@ void path_hash_func(const void *p, struct siphash *state) { } } -int path_compare_func(const void *a, const void *b) { +int path_compare_func(const char *a, const char *b) { return path_compare(a, b); } -const struct hash_ops path_hash_ops = { - .hash = path_hash_func, - .compare = path_compare_func -}; +DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare_func); #endif /* NM_IGNORED */ void trivial_hash_func(const void *p, struct siphash *state) { @@ -80,41 +69,29 @@ int trivial_compare_func(const void *a, const void *b) { const struct hash_ops trivial_hash_ops = { .hash = trivial_hash_func, - .compare = trivial_compare_func + .compare = trivial_compare_func, }; -void uint64_hash_func(const void *p, struct siphash *state) { +void uint64_hash_func(const uint64_t *p, struct siphash *state) { siphash24_compress(p, sizeof(uint64_t), state); } -int uint64_compare_func(const void *_a, const void *_b) { - uint64_t a, b; - a = *(const uint64_t*) _a; - b = *(const uint64_t*) _b; - return CMP(a, b); +int uint64_compare_func(const uint64_t *a, const uint64_t *b) { + return CMP(*a, *b); } -const struct hash_ops uint64_hash_ops = { - .hash = uint64_hash_func, - .compare = uint64_compare_func -}; +DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func); #if 0 /* NM_IGNORED */ #if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) { +void devt_hash_func(const dev_t *p, struct siphash *state) { siphash24_compress(p, sizeof(dev_t), state); } -int devt_compare_func(const void *_a, const void *_b) { - dev_t a, b; - a = *(const dev_t*) _a; - b = *(const dev_t*) _b; - return CMP(a, b); +int devt_compare_func(const dev_t *a, const dev_t *b) { + return CMP(*a, *b); } -const struct hash_ops devt_hash_ops = { - .hash = devt_hash_func, - .compare = devt_compare_func -}; +DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func); #endif #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hash-funcs.h b/src/systemd/src/basic/hash-funcs.h index fa45cfe256..3d2ae4b55e 100644 --- a/src/systemd/src/basic/hash-funcs.h +++ b/src/systemd/src/basic/hash-funcs.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "alloc-util.h" #include "macro.h" #include "siphash24.h" @@ -10,14 +11,74 @@ typedef int (*compare_func_t)(const void *a, const void *b); struct hash_ops { hash_func_t hash; compare_func_t compare; + free_func_t free_key; + free_func_t free_value; }; -void string_hash_func(const void *p, struct siphash *state); -int string_compare_func(const void *a, const void *b) _pure_; +#define _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, free_key_func, free_value_func, scope) \ + _unused_ static void (* UNIQ_T(static_hash_wrapper, uq))(const type *, struct siphash *) = hash_func; \ + _unused_ static int (* UNIQ_T(static_compare_wrapper, uq))(const type *, const type *) = compare_func; \ + scope const struct hash_ops name = { \ + .hash = (hash_func_t) hash_func, \ + .compare = (compare_func_t) compare_func, \ + .free_key = free_key_func, \ + .free_value = free_value_func, \ + } + +#define _DEFINE_FREE_FUNC(uq, type, wrapper_name, func) \ + /* Type-safe free function */ \ + static void UNIQ_T(wrapper_name, uq)(void *a) { \ + type *_a = a; \ + func(_a); \ + } + +#define _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(uq, name, type, hash_func, compare_func, free_func, scope) \ + _DEFINE_FREE_FUNC(uq, type, static_free_wrapper, free_func); \ + _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \ + UNIQ_T(static_free_wrapper, uq), NULL, scope) + +#define _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(uq, name, type, hash_func, compare_func, type_value, free_func, scope) \ + _DEFINE_FREE_FUNC(uq, type_value, static_free_wrapper, free_func); \ + _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \ + NULL, UNIQ_T(static_free_wrapper, uq), scope) + +#define _DEFINE_HASH_OPS_FULL(uq, name, type, hash_func, compare_func, free_key_func, type_value, free_value_func, scope) \ + _DEFINE_FREE_FUNC(uq, type, static_free_key_wrapper, free_key_func); \ + _DEFINE_FREE_FUNC(uq, type_value, static_free_value_wrapper, free_value_func); \ + _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \ + UNIQ_T(static_free_key_wrapper, uq), \ + UNIQ_T(static_free_value_wrapper, uq), scope) + +#define DEFINE_HASH_OPS(name, type, hash_func, compare_func) \ + _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL,) + +#define DEFINE_PRIVATE_HASH_OPS(name, type, hash_func, compare_func) \ + _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL, static) + +#define DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \ + _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func,) + +#define DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \ + _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func, static) + +#define DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \ + _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func,) + +#define DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \ + _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func, static) + +#define DEFINE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \ + _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func,) + +#define DEFINE_PRIVATE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \ + _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func, static) + +void string_hash_func(const char *p, struct siphash *state); +#define string_compare_func strcmp extern const struct hash_ops string_hash_ops; -void path_hash_func(const void *p, struct siphash *state); -int path_compare_func(const void *a, const void *b) _pure_; +void path_hash_func(const char *p, struct siphash *state); +int path_compare_func(const char *a, const char *b) _pure_; extern const struct hash_ops path_hash_ops; /* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings @@ -28,15 +89,15 @@ extern const struct hash_ops trivial_hash_ops; /* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit * values indirectly, since they don't fit in a pointer. */ -void uint64_hash_func(const void *p, struct siphash *state); -int uint64_compare_func(const void *a, const void *b) _pure_; +void uint64_hash_func(const uint64_t *p, struct siphash *state); +int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_; extern const struct hash_ops uint64_hash_ops; /* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on * 64bit archs. Yuck! */ #if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) _pure_; -int devt_compare_func(const void *a, const void *b) _pure_; +void devt_hash_func(const dev_t *p, struct siphash *state) _pure_; +int devt_compare_func(const dev_t *a, const dev_t *b) _pure_; extern const struct hash_ops devt_hash_ops; #else #define devt_hash_func uint64_hash_func diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c index 328c4d3a5a..e31ffaca5e 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -278,7 +278,7 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { }; #if VALGRIND -__attribute__((destructor)) static void cleanup_pools(void) { +_destructor_ static void cleanup_pools(void) { _cleanup_free_ char *t = NULL; int r; @@ -781,7 +781,7 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu h->type = type; h->from_pool = up; - h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; + h->hash_ops = hash_ops ?: &trivial_hash_ops; if (type == HASHMAP_TYPE_ORDERED) { OrderedHashmap *lh = (OrderedHashmap*)h; @@ -850,7 +850,7 @@ int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASH static void hashmap_free_no_clear(HashmapBase *h) { assert(!h->has_indirect); - assert(!h->n_direct_entries); + assert(h->n_direct_entries == 0); #if ENABLE_DEBUG_HASHMAP assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); @@ -866,46 +866,41 @@ static void hashmap_free_no_clear(HashmapBase *h) { free(h); } -HashmapBase *internal_hashmap_free(HashmapBase *h) { - - /* Free the hashmap, but nothing in it */ - +HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { if (h) { - internal_hashmap_clear(h); + internal_hashmap_clear(h, default_free_key, default_free_value); hashmap_free_no_clear(h); } return NULL; } -HashmapBase *internal_hashmap_free_free(HashmapBase *h) { - - /* Free the hashmap and all data objects in it, but not the - * keys */ +void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { + free_func_t free_key, free_value; + if (!h) + return; - if (h) { - internal_hashmap_clear_free(h); - hashmap_free_no_clear(h); - } + free_key = h->hash_ops->free_key ?: default_free_key; + free_value = h->hash_ops->free_value ?: default_free_value; - return NULL; -} + if (free_key || free_value) { -Hashmap *hashmap_free_free_free(Hashmap *h) { + /* If destructor calls are defined, let's destroy things defensively: let's take the item out of the + * hash table, and only then call the destructor functions. If these destructors then try to unregister + * themselves from our hash table a second time, the entry is already gone. */ - /* Free the hashmap and all data and key objects in it */ + while (internal_hashmap_size(h) > 0) { + void *v, *k; - if (h) { - hashmap_clear_free_free(h); - hashmap_free_no_clear(HASHMAP_BASE(h)); - } + v = internal_hashmap_first_key_and_value(h, true, &k); - return NULL; -} + if (free_key) + free_key(k); -void internal_hashmap_clear(HashmapBase *h) { - if (!h) - return; + if (free_value) + free_value(v); + } + } if (h->has_indirect) { free(h->indirect.storage); @@ -923,35 +918,6 @@ void internal_hashmap_clear(HashmapBase *h) { base_set_dirty(h); } -void internal_hashmap_clear_free(HashmapBase *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; - idx = skip_free_buckets(h, idx + 1)) - free(entry_value(h, bucket_at(h, idx))); - - internal_hashmap_clear(h); -} - -void hashmap_clear_free_free(Hashmap *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; - idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { - struct plain_hashmap_entry *e = plain_bucket_at(h, idx); - free((void*)e->b.key); - free(e->value); - } - - internal_hashmap_clear(HASHMAP_BASE(h)); -} - static int resize_buckets(HashmapBase *h, unsigned entries_add); /* @@ -1515,8 +1481,8 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_ return 0; } -void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; +void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { + struct hashmap_base_entry *e; unsigned hash, idx; if (!h) @@ -1527,8 +1493,8 @@ void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { if (idx == IDX_NIL) return NULL; - e = plain_bucket_at(h, idx); - if (e->value != value) + e = bucket_at(h, idx); + if (entry_value(h, e) != value) return NULL; remove_entry(h, idx); @@ -1742,7 +1708,7 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) { } if (r < 0) { - internal_hashmap_free(copy); + internal_hashmap_free(copy, false, false); return NULL; } diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h index bb2a5c76ec..5bf807a76f 100644 --- a/src/systemd/src/basic/hashmap.h +++ b/src/systemd/src/basic/hashmap.h @@ -22,6 +22,8 @@ #define HASH_KEY_SIZE 16 +typedef void* (*hashmap_destroy_t)(void *p); + /* The base type for all hashmap and set types. Many functions in the * implementation take (HashmapBase*) parameters and are run-time polymorphic, * though the API is not meant to be polymorphic (do not call functions @@ -87,25 +89,33 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HA #define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -HashmapBase *internal_hashmap_free(HashmapBase *h); +HashmapBase *internal_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*)internal_hashmap_free(HASHMAP_BASE(h)); + return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); + return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); } -HashmapBase *internal_hashmap_free_free(HashmapBase *h); static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); + return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); + return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); +} + +static inline Hashmap *hashmap_free_free_key(Hashmap *h) { + return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); +} +static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) { + return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); } -Hashmap *hashmap_free_free_free(Hashmap *h); +static inline Hashmap *hashmap_free_free_free(Hashmap *h) { + return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); +} static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); + return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); } IteratedCache *iterated_cache_free(IteratedCache *cache); @@ -181,7 +191,11 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } -void *hashmap_remove_value(Hashmap *h, const void *key, void *value); +void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value); +static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { + return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value); +} + static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); } @@ -258,25 +272,33 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); } -void internal_hashmap_clear(HashmapBase *h); +void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); + internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); + internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } -void internal_hashmap_clear_free(HashmapBase *h); static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); + internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); + internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); } -void hashmap_clear_free_free(Hashmap *h); +static inline void hashmap_clear_free_key(Hashmap *h) { + internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); +} +static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) { + internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); +} + +static inline void hashmap_clear_free_free(Hashmap *h) { + internal_hashmap_clear(HASHMAP_BASE(h), free, free); +} static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - hashmap_clear_free_free(PLAIN_HASHMAP(h)); + internal_hashmap_clear(HASHMAP_BASE(h), free, free); } /* diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c index 0dbdfd5ce7..6a3d4afb46 100644 --- a/src/systemd/src/basic/hostname-util.c +++ b/src/systemd/src/basic/hostname-util.c @@ -73,12 +73,12 @@ int gethostname_strict(char **ret) { return 0; } -static bool hostname_valid_char(char c) { +bool valid_ldh_char(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || - IN_SET(c, '-', '_', '.'); + c == '-'; } /** @@ -94,7 +94,7 @@ static bool hostname_valid_char(char c) { bool hostname_is_valid(const char *s, bool allow_trailing_dot) { unsigned n_dots = 0; const char *p; - bool dot; + bool dot, hyphen; if (isempty(s)) return false; @@ -104,23 +104,34 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { * sequence. Also ensures that the length stays below * HOST_NAME_MAX. */ - for (p = s, dot = true; *p; p++) { + for (p = s, dot = hyphen = true; *p; p++) if (*p == '.') { - if (dot) + if (dot || hyphen) return false; dot = true; + hyphen = false; n_dots++; + + } else if (*p == '-') { + if (dot) + return false; + + dot = false; + hyphen = true; + } else { - if (!hostname_valid_char(*p)) + if (!valid_ldh_char(*p)) return false; dot = false; + hyphen = false; } - } if (dot && (n_dots < 2 || !allow_trailing_dot)) return false; + if (hyphen) + return false; if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on * Linux, but DNS allows domain names @@ -132,29 +143,38 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { char* hostname_cleanup(char *s) { char *p, *d; - bool dot; + bool dot, hyphen; assert(s); - strshorten(s, HOST_NAME_MAX); - - for (p = s, d = s, dot = true; *p; p++) { + for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++) if (*p == '.') { - if (dot) + if (dot || hyphen) continue; *(d++) = '.'; dot = true; - } else if (hostname_valid_char(*p)) { + hyphen = false; + + } else if (*p == '-') { + if (dot) + continue; + + *(d++) = '-'; + dot = false; + hyphen = true; + + } else if (valid_ldh_char(*p)) { *(d++) = *p; dot = false; + hyphen = false; } - } - if (dot && d > s) - d[-1] = 0; - else - *d = 0; + if (d > s && IN_SET(d[-1], '-', '.')) + /* The dot can occur at most once, but we might have multiple + * hyphens, hence the loop */ + d--; + *d = 0; return s; } diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h index 749481723d..7ba386a0fd 100644 --- a/src/systemd/src/basic/hostname-util.h +++ b/src/systemd/src/basic/hostname-util.h @@ -11,6 +11,7 @@ bool hostname_is_set(void); char* gethostname_malloc(void); int gethostname_strict(char **ret); +bool valid_ldh_char(char c) _const_; bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; char* hostname_cleanup(char *s); diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c index 206e00ca93..0f2538e213 100644 --- a/src/systemd/src/basic/in-addr-util.c +++ b/src/systemd/src/basic/in-addr-util.c @@ -366,7 +366,7 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); - return 32 - u32ctz(be32toh(addr->s_addr)); + return 32U - u32ctz(be32toh(addr->s_addr)); } struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { @@ -608,15 +608,12 @@ int in_addr_prefix_from_string_auto_internal( } -void in_addr_data_hash_func(const void *p, struct siphash *state) { - const struct in_addr_data *a = p; - +static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) { siphash24_compress(&a->family, sizeof(a->family), state); siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); } -int in_addr_data_compare_func(const void *a, const void *b) { - const struct in_addr_data *x = a, *y = b; +static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) { int r; r = CMP(x->family, y->family); @@ -626,8 +623,5 @@ int in_addr_data_compare_func(const void *a, const void *b) { return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); } -const struct hash_ops in_addr_data_hash_ops = { - .hash = in_addr_data_hash_func, - .compare = in_addr_data_compare_func, -}; +DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func); #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h index 2f64c31f96..5de85cc422 100644 --- a/src/systemd/src/basic/in-addr-util.h +++ b/src/systemd/src/basic/in-addr-util.h @@ -69,6 +69,4 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) { * See also oss-fuzz#11344. */ #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} }) -void in_addr_data_hash_func(const void *p, struct siphash *state); -int in_addr_data_compare_func(const void *a, const void *b); extern const struct hash_ops in_addr_data_hash_ops; diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h index 040680c30a..f7f97000e0 100644 --- a/src/systemd/src/basic/list.h +++ b/src/systemd/src/basic/list.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "macro.h" + /* The head of the linked list. Use this in the structure that shall * contain the head of the linked list */ #define LIST_HEAD(t,name) \ @@ -13,8 +15,8 @@ /* Initialize the list's head */ #define LIST_HEAD_INIT(head) \ do { \ - (head) = NULL; } \ - while (false) + (head) = NULL; \ + } while (false) /* Initialize a list item */ #define LIST_INIT(name,item) \ diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h index d1775f2ae9..6a1fdab537 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -2,33 +2,38 @@ #pragma once #include <assert.h> +#include <errno.h> #include <inttypes.h> #include <stdbool.h> #include <sys/param.h> #include <sys/sysmacros.h> #include <sys/types.h> -#define _printf_(a, b) __attribute__ ((__format__(printf, a, b))) +#define _printf_(a, b) __attribute__((__format__(printf, a, b))) #ifdef __clang__ # define _alloc_(...) #else -# define _alloc_(...) __attribute__ ((__alloc_size__(__VA_ARGS__))) +# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) #endif -#define _sentinel_ __attribute__ ((__sentinel__)) -#define _unused_ __attribute__ ((__unused__)) -#define _destructor_ __attribute__ ((__destructor__)) -#define _pure_ __attribute__ ((__pure__)) -#define _const_ __attribute__ ((__const__)) -#define _deprecated_ __attribute__ ((__deprecated__)) -#define _packed_ __attribute__ ((__packed__)) -#define _malloc_ __attribute__ ((__malloc__)) -#define _weak_ __attribute__ ((__weak__)) +#define _sentinel_ __attribute__((__sentinel__)) +#define _section_(x) __attribute__((__section__(x))) +#define _used_ __attribute__((__used__)) +#define _unused_ __attribute__((__unused__)) +#define _destructor_ __attribute__((__destructor__)) +#define _pure_ __attribute__((__pure__)) +#define _const_ __attribute__((__const__)) +#define _deprecated_ __attribute__((__deprecated__)) +#define _packed_ __attribute__((__packed__)) +#define _malloc_ __attribute__((__malloc__)) +#define _weak_ __attribute__((__weak__)) #define _likely_(x) (__builtin_expect(!!(x), 1)) #define _unlikely_(x) (__builtin_expect(!!(x), 0)) -#define _public_ __attribute__ ((__visibility__("default"))) -#define _hidden_ __attribute__ ((__visibility__("hidden"))) +#define _public_ __attribute__((__visibility__("default"))) +#define _hidden_ __attribute__((__visibility__("hidden"))) #define _weakref_(x) __attribute__((__weakref__(#x))) +#define _align_(x) __attribute__((__aligned__(x))) #define _alignas_(x) __attribute__((__aligned__(__alignof(x)))) +#define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) #define _cleanup_(x) __attribute__((__cleanup__(x))) #if __GNUC__ >= 7 #define _fallthrough_ __attribute__((__fallthrough__)) @@ -56,6 +61,29 @@ # endif #endif +#if !defined(HAS_FEATURE_ADDRESS_SANITIZER) +# ifdef __SANITIZE_ADDRESS__ +# define HAS_FEATURE_ADDRESS_SANITIZER 1 +# elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define HAS_FEATURE_ADDRESS_SANITIZER 1 +# endif +# endif +# if !defined(HAS_FEATURE_ADDRESS_SANITIZER) +# define HAS_FEATURE_ADDRESS_SANITIZER 0 +# endif +#endif + +/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global + * variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since + * our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be + * padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */ +#if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__) +#define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__)) +#else +#define _variable_no_sanitize_address_ +#endif + #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) /* Temporarily disable some warnings */ #define DISABLE_WARNING_FORMAT_NONLITERAL \ @@ -255,11 +283,12 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { * computation should be possible in the given type. Therefore, we use * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the * quotient and the remainder, so both should be equally fast. */ -#define DIV_ROUND_UP(_x, _y) \ +#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y)) +#define __DIV_ROUND_UP(xq, x, yq, y) \ ({ \ - const typeof(_x) __x = (_x); \ - const typeof(_y) __y = (_y); \ - (__x / __y + !!(__x % __y)); \ + const typeof(x) UNIQ_T(X, xq) = (x); \ + const typeof(y) UNIQ_T(Y, yq) = (y); \ + (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \ }) #ifdef __COVERITY__ @@ -466,6 +495,11 @@ static inline int __coverity_check__(int condition) { #endif #endif +#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \ + static inline void name(type *p) { \ + func(p); \ + } + #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c index 09ae085511..1f0b711113 100644 --- a/src/systemd/src/basic/parse-util.c +++ b/src/systemd/src/basic/parse-util.c @@ -4,6 +4,7 @@ #include <errno.h> #include <inttypes.h> +#include <linux/oom.h> #include <locale.h> #include <stdio.h> #include <stdlib.h> @@ -18,10 +19,12 @@ #include "missing.h" #include "parse-util.h" #include "process-util.h" +#include "stat-util.h" #include "string-util.h" int parse_boolean(const char *v) { - assert(v); + if (!v) + return -EINVAL; if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) return 1; @@ -715,18 +718,51 @@ int parse_ip_port(const char *s, uint16_t *ret) { return 0; } +int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) { + unsigned l, h; + int r; + + r = parse_range(s, &l, &h); + if (r < 0) + return r; + + if (l <= 0 || l > 65535 || h <= 0 || h > 65535) + return -EINVAL; + + if (h < l) + return -EINVAL; + + *low = l; + *high = h; + + return 0; +} + int parse_dev(const char *s, dev_t *ret) { + const char *major; unsigned x, y; - dev_t d; + size_t n; + int r; - if (sscanf(s, "%u:%u", &x, &y) != 2) + n = strspn(s, DIGITS); + if (n == 0) return -EINVAL; - - d = makedev(x, y); - if ((unsigned) major(d) != x || (unsigned) minor(d) != y) + if (s[n] != ':') return -EINVAL; - *ret = d; + major = strndupa(s, n); + r = safe_atou(major, &x); + if (r < 0) + return r; + + r = safe_atou(s + n + 1, &y); + if (r < 0) + return r; + + if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y)) + return -ERANGE; + + *ret = makedev(x, y); return 0; } diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h index f3267f4cfe..e47641b429 100644 --- a/src/systemd/src/basic/parse-util.h +++ b/src/systemd/src/basic/parse-util.h @@ -115,5 +115,6 @@ int parse_permille(const char *p); int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); +int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high); int parse_oom_score_adjust(const char *s, int *ret); diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index dc37e04dd7..1446ecdc85 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -113,7 +113,7 @@ int path_make_absolute_cwd(const char *p, char **ret) { if (r < 0) return r; - c = path_join(NULL, cwd, p); + c = path_join(cwd, p); } if (!c) return -ENOMEM; @@ -486,18 +486,62 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) { return path_equal(a, b) || files_same(a, b, flags) > 0; } -char* path_join(const char *root, const char *path, const char *rest) { - assert(path); +char* path_join_internal(const char *first, ...) { + char *joined, *q; + const char *p; + va_list ap; + bool slash; + size_t sz; + + /* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin + * already with one so that it is unnecessary. Note that slashes which are already duplicate won't be + * removed. The string returned is hence always equal to or longer than the sum of the lengths of each + * individual string. + * + * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some + * are optional. + * + * Examples: + * + * path_join("foo", "bar") → "foo/bar" + * path_join("foo/", "bar") → "foo/bar" + * path_join("", "foo", "", "bar", "") → "foo/bar" */ + + sz = strlen_ptr(first); + va_start(ap, first); + while ((p = va_arg(ap, char*)) != (const char*) -1) + if (!isempty(p)) + sz += 1 + strlen(p); + va_end(ap); + + joined = new(char, sz + 1); + if (!joined) + return NULL; - if (!isempty(root)) - return strjoin(root, endswith(root, "/") ? "" : "/", - path[0] == '/' ? path+1 : path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest); - else - return strjoin(path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest); + if (!isempty(first)) { + q = stpcpy(joined, first); + slash = endswith(first, "/"); + } else { + /* Skip empty items */ + joined[0] = 0; + q = joined; + slash = true; /* no need to generate a slash anymore */ + } + + va_start(ap, first); + while ((p = va_arg(ap, char*)) != (const char*) -1) { + if (isempty(p)) + continue; + + if (!slash && p[0] != '/') + *(q++) = '/'; + + q = stpcpy(q, p); + slash = endswith(p, "/"); + } + va_end(ap); + + return joined; } int find_binary(const char *name, char **ret) { @@ -755,6 +799,9 @@ const char *last_path_component(const char *path) { unsigned l, k; + if (!path) + return NULL; + l = k = strlen(path); if (l == 0) /* special case — an empty string */ return path; @@ -770,6 +817,37 @@ const char *last_path_component(const char *path) { return path + k; } + +int path_extract_filename(const char *p, char **ret) { + _cleanup_free_ char *a = NULL; + const char *c, *e = NULL, *q; + + /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes + * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */ + + if (!p) + return -EINVAL; + + c = last_path_component(p); + + for (q = c; *q != 0; q++) + if (*q != '/') + e = q + 1; + + if (!e) /* no valid character? */ + return -EINVAL; + + a = strndup(c, e - c); + if (!a) + return -ENOMEM; + + if (!filename_is_valid(a)) + return -EINVAL; + + *ret = TAKE_PTR(a); + + return 0; +} #endif /* NM_IGNORED */ bool filename_is_valid(const char *p) { @@ -926,8 +1004,7 @@ bool valid_device_allow_pattern(const char *path) { /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= * accept it */ - if (startswith(path, "block-") || - startswith(path, "char-")) + if (STARTSWITH_SET(path, "block-", "char-")) return true; return valid_device_node_path(path); @@ -1071,6 +1148,13 @@ int path_simplify_and_warn( return -EINVAL; } + if (!path_is_valid(path)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "%s= path has invalid length (%zu bytes)%s.", + lvalue, strlen(path), fatal ? "" : ", ignoring"); + return -EINVAL; + } + return 0; } #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 4a4bd4cd51..56ebd59c6f 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -7,6 +7,7 @@ #include "macro.h" #include "string-util.h" +#include "strv.h" #include "time-util.h" #if 0 /* NM_IGNORED */ @@ -50,7 +51,9 @@ char* path_startswith(const char *path, const char *prefix) _pure_; int path_compare(const char *a, const char *b) _pure_; bool path_equal(const char *a, const char *b) _pure_; bool path_equal_or_files_same(const char *a, const char *b, int flags); -char* path_join(const char *root, const char *path, const char *rest); +char* path_join_internal(const char *first, ...); +#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, (const char*) -1) + char* path_simplify(char *path, bool kill_dots); static inline bool path_equal_ptr(const char *a, const char *b) { @@ -72,13 +75,13 @@ static inline bool path_equal_ptr(const char *a, const char *b) { #define PATH_STARTSWITH_SET(p, ...) \ ({ \ - char **s; \ - bool _found = false; \ - STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ - if (path_startswith(p, *s)) { \ - _found = true; \ - break; \ - } \ + const char *_p = (p); \ + char *_found = NULL, **_i; \ + STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ + _found = path_startswith(_p, *_i); \ + if (_found) \ + break; \ + } \ _found; \ }) @@ -96,12 +99,24 @@ 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 * directory. Excludes the specified directory itself */ -#define PATH_FOREACH_PREFIX(prefix, path) \ - for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) +#define PATH_FOREACH_PREFIX(prefix, path) \ + for (char *_slash = ({ \ + path_simplify(strcpy(prefix, path), false); \ + streq(prefix, "/") ? NULL : strrchr(prefix, '/'); \ + }); \ + _slash && ((*_slash = 0), true); \ + _slash = strrchr((prefix), '/')) /* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ -#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ - for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) +#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ + for (char *_slash = ({ \ + path_simplify(strcpy(prefix, path), false); \ + if (streq(prefix, "/")) \ + prefix[0] = 0; \ + strrchr(prefix, 0); \ + }); \ + _slash && ((*_slash = 0), true); \ + _slash = strrchr((prefix), '/')) char *prefix_root(const char *root, const char *path); @@ -134,6 +149,7 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar char* dirname_malloc(const char *path); const char *last_path_component(const char *path); +int path_extract_filename(const char *p, char **ret); bool filename_is_valid(const char *p) _pure_; bool path_is_valid(const char *p) _pure_; diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c index 3af3402504..14e29c52e4 100644 --- a/src/systemd/src/basic/prioq.c +++ b/src/systemd/src/basic/prioq.c @@ -67,9 +67,6 @@ int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) { } static void swap(Prioq *q, unsigned j, unsigned k) { - void *saved_data; - unsigned *saved_idx; - assert(q); assert(j < q->n_items); assert(k < q->n_items); @@ -77,12 +74,8 @@ static void swap(Prioq *q, unsigned j, unsigned k) { assert(!q->items[j].idx || *(q->items[j].idx) == j); assert(!q->items[k].idx || *(q->items[k].idx) == k); - saved_data = q->items[j].data; - saved_idx = q->items[j].idx; - q->items[j].data = q->items[k].data; - q->items[j].idx = q->items[k].idx; - q->items[k].data = saved_data; - q->items[k].idx = saved_idx; + SWAP_TWO(q->items[j].data, q->items[k].data); + SWAP_TWO(q->items[j].idx, q->items[k].idx); if (q->items[j].idx) *q->items[j].idx = j; diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c index 4171a23777..bf05b2e8ef 100644 --- a/src/systemd/src/basic/process-util.c +++ b/src/systemd/src/basic/process-util.c @@ -39,6 +39,7 @@ #include "missing.h" #include "process-util.h" #include "raw-clone.h" +#include "rlimit-util.h" #include "signal-util.h" #include "stat-util.h" #include "string-table.h" @@ -604,12 +605,14 @@ int get_process_root(pid_t pid, char **root) { return get_process_link_contents(p, root); } +#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U) + int get_process_environ(pid_t pid, char **env) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *outcome = NULL; - int c; - const char *p; size_t allocated = 0, sz = 0; + const char *p; + int r; assert(pid >= 0); assert(env); @@ -625,23 +628,28 @@ int get_process_environ(pid_t pid, char **env) { (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - while ((c = fgetc(f)) != EOF) { + for (;;) { + char c; + + if (sz >= ENVIRONMENT_BLOCK_MAX) + return -ENOBUFS; + if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) return -ENOMEM; + r = safe_fgetc(f, &c); + if (r < 0) + return r; + if (r == 0) + break; + if (c == '\0') outcome[sz++] = '\n'; else sz += cescape_char(c, outcome + sz); } - if (!outcome) { - outcome = strdup(""); - if (!outcome) - return -ENOMEM; - } else - outcome[sz] = '\0'; - + outcome[sz] = '\0'; *env = TAKE_PTR(outcome); return 0; @@ -874,9 +882,9 @@ int kill_and_sigcont(pid_t pid, int sig) { int getenv_for_pid(pid_t pid, const char *field, char **ret) { _cleanup_fclose_ FILE *f = NULL; char *value = NULL; - bool done = false; const char *path; - size_t l; + size_t l, sum = 0; + int r; assert(pid >= 0); assert(field); @@ -899,6 +907,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) { return 1; } + if (!pid_is_valid(pid)) + return -EINVAL; + path = procfs_file_alloca(pid, "environ"); f = fopen(path, "re"); @@ -912,24 +923,19 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) { (void) __fsetlocking(f, FSETLOCKING_BYCALLER); l = strlen(field); + for (;;) { + _cleanup_free_ char *line = NULL; - do { - char line[LINE_MAX]; - size_t i; - - for (i = 0; i < sizeof(line)-1; i++) { - int c; + if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */ + return -ENOBUFS; - c = getc(f); - if (_unlikely_(c == EOF)) { - done = true; - break; - } else if (c == 0) - break; + r = read_nul_string(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) /* EOF */ + break; - line[i] = c; - } - line[i] = 0; + sum += r; if (strneq(line, field, l) && line[l] == '=') { value = strdup(line + l + 1); @@ -939,8 +945,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) { *ret = value; return 1; } - - } while (!done); + } *ret = NULL; return 0; @@ -1180,7 +1185,7 @@ void reset_cached_pid(void) { * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against * libpthread, as it is part of glibc anyway. */ extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle); -extern void* __dso_handle __attribute__ ((__weak__)); +extern void* __dso_handle _weak_; pid_t getpid_cached(void) { static bool installed = false; @@ -1259,25 +1264,17 @@ int safe_fork_full( original_pid = getpid_cached(); if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { - /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can * be sure that SIGTERMs are not lost we might send to the child. */ - if (sigfillset(&ss) < 0) - return log_full_errno(prio, errno, "Failed to reset signal set: %m"); - + assert_se(sigfillset(&ss) >= 0); block_signals = true; } else if (flags & FORK_WAIT) { - /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ - if (sigemptyset(&ss) < 0) - return log_full_errno(prio, errno, "Failed to clear signal set: %m"); - - if (sigaddset(&ss, SIGCHLD) < 0) - return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m"); - + assert_se(sigemptyset(&ss) >= 0); + assert_se(sigaddset(&ss, SIGCHLD) >= 0); block_signals = true; } @@ -1410,6 +1407,14 @@ int safe_fork_full( } } + if (flags & FORK_RLIMIT_NOFILE_SAFE) { + r = rlimit_nofile_safe(); + if (r < 0) { + log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m"); + _exit(EXIT_FAILURE); + } + } + if (ret_pid) *ret_pid = getpid_cached(); @@ -1521,6 +1526,8 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret safe_close_above_stdio(fd); } + (void) rlimit_nofile_safe(); + /* Count arguments */ va_start(ap, path); for (n = 0; va_arg(ap, char*); n++) diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h index fea152cecf..0fad4e1ee9 100644 --- a/src/systemd/src/basic/process-util.h +++ b/src/systemd/src/basic/process-util.h @@ -144,15 +144,16 @@ void reset_cached_pid(void); int must_be_root(void); typedef enum ForkFlags { - FORK_RESET_SIGNALS = 1 << 0, - FORK_CLOSE_ALL_FDS = 1 << 1, - FORK_DEATHSIG = 1 << 2, - FORK_NULL_STDIO = 1 << 3, - FORK_REOPEN_LOG = 1 << 4, - FORK_LOG = 1 << 5, - FORK_WAIT = 1 << 6, - FORK_NEW_MOUNTNS = 1 << 7, - FORK_MOUNTNS_SLAVE = 1 << 8, + FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ + FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ + FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */ + FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */ + FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */ + FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */ + FORK_WAIT = 1 << 6, /* Wait until child exited */ + FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */ + FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */ + FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c index f69ca0f0f4..39ca85eebb 100644 --- a/src/systemd/src/basic/random-util.c +++ b/src/systemd/src/basic/random-util.c @@ -9,7 +9,6 @@ #include <elf.h> #include <errno.h> #include <fcntl.h> -#include <linux/random.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h index 0d99d5550d..2a80632b79 100644 --- a/src/systemd/src/basic/set.h +++ b/src/systemd/src/basic/set.h @@ -9,13 +9,11 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { - internal_hashmap_free(HASHMAP_BASE(s)); - return NULL; + return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL); } static inline Set *set_free_free(Set *s) { - internal_hashmap_free_free(HASHMAP_BASE(s)); - return NULL; + return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL); } /* no set_free_free_free */ @@ -76,11 +74,11 @@ static inline unsigned set_buckets(Set *s) { bool set_iterate(Set *s, Iterator *i, void **value); static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s)); + internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL); } static inline void set_clear_free(Set *s) { - internal_hashmap_clear_free(HASHMAP_BASE(s)); + internal_hashmap_clear(HASHMAP_BASE(s), free, NULL); } /* no set_clear_free_free */ diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index 4476f3dc7a..7fcedf3714 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -17,6 +17,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "escape.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -114,7 +115,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { size_t l; l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here + if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here * when parsing, even though abstract namespace sockets * explicitly allow embedded NUL bytes and don't consider * them special. But it's simply annoying to debug such @@ -264,9 +265,12 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) { return 0; } -int socket_address_verify(const SocketAddress *a) { +int socket_address_verify(const SocketAddress *a, bool strict) { assert(a); + /* With 'strict' we enforce additional sanity constraints which are not set by the standard, + * but should only apply to sockets we create ourselves. */ + switch (socket_address_family(a)) { case AF_INET: @@ -296,19 +300,20 @@ int socket_address_verify(const SocketAddress *a) { case AF_UNIX: if (a->size < offsetof(struct sockaddr_un, sun_path)) return -EINVAL; - if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will - * append a NUL byte if we have path sockets that are above - * sun_path' full size */ + if (a->size > sizeof(struct sockaddr_un) + !strict) + /* If !strict, allow one extra byte, since getsockname() on Linux will append + * a NUL byte if we have path sockets that are above sun_path's full size. */ return -EINVAL; if (a->size > offsetof(struct sockaddr_un, sun_path) && - a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */ - + a->sockaddr.un.sun_path[0] != 0 && + strict) { + /* Only validate file system sockets here, and only in strict mode */ const char *e; e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); if (e) { - /* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */ + /* If there's an embedded NUL byte, make sure the size of the socket address matches it */ if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) return -EINVAL; } else { @@ -356,7 +361,10 @@ int socket_address_print(const SocketAddress *a, char **ret) { assert(a); assert(ret); - r = socket_address_verify(a); + r = socket_address_verify(a, false); /* We do non-strict validation, because we want to be + * able to pretty-print any socket the kernel considers + * valid. We still need to do validation to know if we + * can meaningfully print the address. */ if (r < 0) return r; @@ -389,8 +397,8 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { assert(b); /* Invalid addresses are unequal to all */ - if (socket_address_verify(a) < 0 || - socket_address_verify(b) < 0) + if (socket_address_verify(a, false) < 0 || + socket_address_verify(b, false) < 0) return false; if (a->type != b->type) @@ -574,7 +582,13 @@ int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { } } -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { +int sockaddr_pretty( + const struct sockaddr *_sa, + socklen_t salen, + bool translate_ipv6, + bool include_port, + char **ret) { + union sockaddr_union *sa = (union sockaddr_union*) _sa; char *p; int r; @@ -645,42 +659,51 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ } case AF_UNIX: - if (salen <= offsetof(struct sockaddr_un, sun_path)) { + if (salen <= offsetof(struct sockaddr_un, sun_path) || + (sa->un.sun_path[0] == 0 && salen == offsetof(struct sockaddr_un, sun_path) + 1)) + /* The name must have at least one character (and the leading NUL does not count) */ p = strdup("<unnamed>"); - if (!p) - return -ENOMEM; - - } else if (sa->un.sun_path[0] == 0) { - /* abstract */ - - /* FIXME: We assume we can print the - * socket path here and that it hasn't - * more than one NUL byte. That is - * actually an invalid assumption */ - - p = new(char, sizeof(sa->un.sun_path)+1); - if (!p) - return -ENOMEM; + else { + /* Note that we calculate the path pointer here through the .un_buffer[] field, in order to + * outtrick bounds checking tools such as ubsan, which are too smart for their own good: on + * Linux the kernel may return sun_path[] data one byte longer than the declared size of the + * field. */ + char *path = (char*) sa->un_buffer + offsetof(struct sockaddr_un, sun_path); + size_t path_len = salen - offsetof(struct sockaddr_un, sun_path); + + if (path[0] == 0) { + /* Abstract socket. When parsing address information from, we + * explicitly reject overly long paths and paths with embedded NULs. + * But we might get such a socket from the outside. Let's return + * something meaningful and printable in this case. */ + + _cleanup_free_ char *e = NULL; + + e = cescape_length(path + 1, path_len - 1); + if (!e) + return -ENOMEM; - p[0] = '@'; - memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1); - p[sizeof(sa->un.sun_path)] = 0; + p = strjoin("@", e); + } else { + if (path[path_len - 1] == '\0') + /* We expect a terminating NUL and don't print it */ + path_len --; - } else { - p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path)); - if (!p) - return -ENOMEM; + p = cescape_length(path, path_len); + } } + if (!p) + return -ENOMEM; break; case AF_VSOCK: - if (include_port) - r = asprintf(&p, - "vsock:%u:%u", - sa->vm.svm_cid, - sa->vm.svm_port); - else + if (include_port) { + if (sa->vm.svm_cid == VMADDR_CID_ANY) + r = asprintf(&p, "vsock::%u", sa->vm.svm_port); + else + r = asprintf(&p, "vsock:%u:%u", sa->vm.svm_cid, sa->vm.svm_port); + } else r = asprintf(&p, "vsock:%u", sa->vm.svm_cid); if (r < 0) return -ENOMEM; diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index 1ac30de5a3..d2246a8eb8 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <inttypes.h> +#include <linux/netlink.h> +#include <linux/if_infiniband.h> +#include <linux/if_packet.h> #include <netinet/ether.h> #include <netinet/in.h> #include <stdbool.h> @@ -8,13 +12,10 @@ #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> -#include <linux/netlink.h> -#include <linux/if_infiniband.h> -#include <linux/if_packet.h> #include "macro.h" -#include "missing.h" -#include "util.h" +#include "missing_socket.h" +#include "sparse-endian.h" union sockaddr_union { /* The minimal, abstract version */ @@ -72,7 +73,7 @@ int socket_address_parse(SocketAddress *a, const char *s); int socket_address_parse_and_warn(SocketAddress *a, const char *s); int socket_address_parse_netlink(SocketAddress *a, const char *s); int socket_address_print(const SocketAddress *a, char **p); -int socket_address_verify(const SocketAddress *a) _pure_; +int socket_address_verify(const SocketAddress *a, bool strict) _pure_; int sockaddr_un_unlink(const struct sockaddr_un *sa); diff --git a/src/systemd/src/basic/stat-util.c b/src/systemd/src/basic/stat-util.c index 8cae1e1e3f..e77f40aee5 100644 --- a/src/systemd/src/basic/stat-util.c +++ b/src/systemd/src/basic/stat-util.c @@ -12,11 +12,13 @@ #include <sys/types.h> #include <unistd.h> +#include "alloc-util.h" #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" #include "macro.h" #include "missing.h" +#include "parse-util.h" #include "stat-util.h" #include "string-util.h" @@ -300,3 +302,124 @@ int fd_verify_regular(int fd) { return stat_verify_regular(&st); } + +#if 0 /* NM_IGNORED */ +int stat_verify_directory(const struct stat *st) { + assert(st); + + if (S_ISLNK(st->st_mode)) + return -ELOOP; + + if (!S_ISDIR(st->st_mode)) + return -ENOTDIR; + + return 0; +} + +int fd_verify_directory(int fd) { + struct stat st; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + return stat_verify_directory(&st); +} + +int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) { + const char *t; + + /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */ + + if (S_ISCHR(mode)) + t = "char"; + else if (S_ISBLK(mode)) + t = "block"; + else + return -ENODEV; + + if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0) + return -ENOMEM; + + return 0; + +} + +int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) { + _cleanup_free_ char *p = NULL; + int r; + + /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */ + + assert(ret); + + if (major(devno) == 0 && minor(devno) == 0) { + char *s; + + /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in + * /dev/block/ and /dev/char/, hence we handle them specially here. */ + + if (S_ISCHR(mode)) + s = strdup("/run/systemd/inaccessible/chr"); + else if (S_ISBLK(mode)) + s = strdup("/run/systemd/inaccessible/blk"); + else + return -ENODEV; + + if (!s) + return -ENOMEM; + + *ret = s; + return 0; + } + + r = device_path_make_major_minor(mode, devno, &p); + if (r < 0) + return r; + + return chase_symlinks(p, NULL, 0, ret); +} + +int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) { + mode_t mode; + dev_t devno; + int r; + + /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/ + * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device + * path cannot be parsed like this. */ + + if (path_equal(path, "/run/systemd/inaccessible/chr")) { + mode = S_IFCHR; + devno = makedev(0, 0); + } else if (path_equal(path, "/run/systemd/inaccessible/blk")) { + mode = S_IFBLK; + devno = makedev(0, 0); + } else { + const char *w; + + w = path_startswith(path, "/dev/block/"); + if (w) + mode = S_IFBLK; + else { + w = path_startswith(path, "/dev/char/"); + if (!w) + return -ENODEV; + + mode = S_IFCHR; + } + + r = parse_dev(w, &devno); + if (r < 0) + return r; + } + + if (ret_mode) + *ret_mode = mode; + if (ret_devno) + *ret_devno = devno; + + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/stat-util.h b/src/systemd/src/basic/stat-util.h index 1a725f1da0..0a08e642b5 100644 --- a/src/systemd/src/basic/stat-util.h +++ b/src/systemd/src/basic/stat-util.h @@ -59,3 +59,29 @@ int path_is_temporary_fs(const char *path); int stat_verify_regular(const struct stat *st); int fd_verify_regular(int fd); + +int stat_verify_directory(const struct stat *st); +int fd_verify_directory(int fd); + +/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the + * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of + * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of + * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as + * such a test would be pointless in such a case.) */ + +#define DEVICE_MAJOR_VALID(x) \ + ({ \ + typeof(x) _x = (x), _y = 0; \ + _x >= _y && _x < (UINT32_C(1) << 12); \ + \ + }) + +#define DEVICE_MINOR_VALID(x) \ + ({ \ + typeof(x) _x = (x), _y = 0; \ + _x >= _y && _x < (UINT32_C(1) << 20); \ + }) + +int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret); +int device_path_make_canonical(mode_t mode, dev_t devno, char **ret); +int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno); diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h index 5f1803d87d..aa4cd4aaca 100644 --- a/src/systemd/src/basic/strv.h +++ b/src/systemd/src/basic/strv.h @@ -144,6 +144,18 @@ void strv_print(char **l); _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ }) +#define STARTSWITH_SET(p, ...) \ + ({ \ + const char *_p = (p); \ + char *_found = NULL, **_i; \ + STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ + _found = startswith(_p, *_i); \ + if (_found) \ + break; \ + } \ + _found; \ + }) + #define FOREACH_STRING(x, ...) \ for (char **_l = ({ \ char **_ll = STRV_MAKE(__VA_ARGS__); \ diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index 3465932f89..a2f616d148 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -22,6 +22,7 @@ #include "io-util.h" #include "log.h" #include "macro.h" +#include "missing_timerfd.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -1391,9 +1392,7 @@ int get_timezone(char **tz) { if (r < 0) return r; /* returns EINVAL if not a symlink */ - e = path_startswith(t, "/usr/share/zoneinfo/"); - if (!e) - e = path_startswith(t, "../usr/share/zoneinfo/"); + e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/"); if (!e) return -EINVAL; diff --git a/src/systemd/src/basic/tmpfile-util.c b/src/systemd/src/basic/tmpfile-util.c new file mode 100644 index 0000000000..b40da5dc8d --- /dev/null +++ b/src/systemd/src/basic/tmpfile-util.c @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "nm-sd-adapt.h" + +#include <sys/mman.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "hexdecoct.h" +#include "macro.h" +#include "memfd-util.h" +#include "missing_syscall.h" +#include "path-util.h" +#include "process-util.h" +#include "random-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "tmpfile-util.h" +#include "umask-util.h" + +int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { + FILE *f; + char *t; + int r, fd; + + assert(path); + assert(_f); + assert(_temp_path); + + r = tempfn_xxxxxx(path, NULL, &t); + if (r < 0) + return r; + + fd = mkostemp_safe(t); + if (fd < 0) { + free(t); + return -errno; + } + + f = fdopen(fd, "w"); + if (!f) { + unlink_noerrno(t); + free(t); + safe_close(fd); + return -errno; + } + + *_f = f; + *_temp_path = t; + + return 0; +} + +/* This is much like mkostemp() but is subject to umask(). */ +int mkostemp_safe(char *pattern) { + _cleanup_umask_ mode_t u = 0; + int fd; + + assert(pattern); + + u = umask(077); + + fd = mkostemp(pattern, O_CLOEXEC); + if (fd < 0) + return -errno; + + return fd; +} + +#if 0 /* NM_IGNORED */ +int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { + int fd; + FILE *f; + + fd = mkostemp_safe(pattern); + if (fd < 0) + return fd; + + f = fdopen(fd, mode); + if (!f) { + safe_close(fd); + return -errno; + } + + *ret_f = f; + return 0; +} +#endif /* NM_IGNORED */ + +int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { + const char *fn; + char *t; + + assert(ret); + + if (isempty(p)) + return -EINVAL; + if (path_equal(p, "/")) + return -EINVAL; + + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#<extra>waldoXXXXXX + */ + + fn = basename(p); + if (!filename_is_valid(fn)) + return -EINVAL; + + extra = strempty(extra); + + t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); + if (!t) + return -ENOMEM; + + strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); + + *ret = path_simplify(t, false); + return 0; +} + +#if 0 /* NM_IGNORED */ +int tempfn_random(const char *p, const char *extra, char **ret) { + const char *fn; + char *t, *x; + uint64_t u; + unsigned i; + + assert(ret); + + if (isempty(p)) + return -EINVAL; + if (path_equal(p, "/")) + return -EINVAL; + + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#<extra>waldobaa2a261115984a9 + */ + + fn = basename(p); + if (!filename_is_valid(fn)) + return -EINVAL; + + extra = strempty(extra); + + t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); + if (!t) + return -ENOMEM; + + x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn); + + u = random_u64(); + for (i = 0; i < 16; i++) { + *(x++) = hexchar(u & 0xF); + u >>= 4; + } + + *x = 0; + + *ret = path_simplify(t, false); + return 0; +} + +int tempfn_random_child(const char *p, const char *extra, char **ret) { + char *t, *x; + uint64_t u; + unsigned i; + int r; + + assert(ret); + + /* Turns this: + * /foo/bar/waldo + * Into this: + * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 + */ + + if (!p) { + r = tmp_dir(&p); + if (r < 0) + return r; + } + + extra = strempty(extra); + + t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); + if (!t) + return -ENOMEM; + + if (isempty(p)) + x = stpcpy(stpcpy(t, ".#"), extra); + else + x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); + + u = random_u64(); + for (i = 0; i < 16; i++) { + *(x++) = hexchar(u & 0xF); + u >>= 4; + } + + *x = 0; + + *ret = path_simplify(t, false); + return 0; +} + +int open_tmpfile_unlinkable(const char *directory, int flags) { + char *p; + int fd, r; + + if (!directory) { + r = tmp_dir(&directory); + if (r < 0) + return r; + } else if (isempty(directory)) + return -EINVAL; + + /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ + + /* Try O_TMPFILE first, if it is supported */ + fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); + if (fd >= 0) + return fd; + + /* Fall back to unguessable name + unlinking */ + p = strjoina(directory, "/systemd-tmp-XXXXXX"); + + fd = mkostemp_safe(p); + if (fd < 0) + return fd; + + (void) unlink(p); + + return fd; +} + +int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { + _cleanup_free_ char *tmp = NULL; + int r, fd; + + assert(target); + assert(ret_path); + + /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ + 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 + * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ + + fd = open_parent(target, O_TMPFILE|flags, 0640); + if (fd >= 0) { + *ret_path = NULL; + return fd; + } + + log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target); + + r = tempfn_random(target, NULL, &tmp); + if (r < 0) + return r; + + fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); + if (fd < 0) + return -errno; + + *ret_path = TAKE_PTR(tmp); + + return fd; +} + +int link_tmpfile(int fd, const char *path, const char *target) { + int r; + + assert(fd >= 0); + assert(target); + + /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd + * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported + * on the directory, and renameat2() is used instead. + * + * Note that in both cases we will not replace existing files. This is because linkat() does not support this + * operation currently (renameat2() does), and there is no nice way to emulate this. */ + + if (path) { + r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target); + if (r < 0) + return r; + } else { + char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + + xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); + + if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) + return -errno; + } + + return 0; +} + +int mkdtemp_malloc(const char *template, char **ret) { + _cleanup_free_ char *p = NULL; + int r; + + assert(ret); + + if (template) + p = strdup(template); + else { + const char *tmp; + + r = tmp_dir(&tmp); + if (r < 0) + return r; + + p = strjoin(tmp, "/XXXXXX"); + } + if (!p) + return -ENOMEM; + + if (!mkdtemp(p)) + return -errno; + + *ret = TAKE_PTR(p); + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/tmpfile-util.h b/src/systemd/src/basic/tmpfile-util.h new file mode 100644 index 0000000000..802c85d6d9 --- /dev/null +++ b/src/systemd/src/basic/tmpfile-util.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdio.h> + +int fopen_temporary(const char *path, FILE **_f, char **_temp_path); +int mkostemp_safe(char *pattern); +int fmkostemp_safe(char *pattern, const char *mode, FILE**_f); + +int tempfn_xxxxxx(const char *p, const char *extra, char **ret); +int tempfn_random(const char *p, const char *extra, char **ret); +int tempfn_random_child(const char *p, const char *extra, char **ret); + +int open_tmpfile_unlinkable(const char *directory, int flags); +int open_tmpfile_linkable(const char *target, int flags, char **ret_path); + +int link_tmpfile(int fd, const char *path, const char *target); + +int mkdtemp_malloc(const char *template, char **ret); diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h index 628680ef53..eedfebe39c 100644 --- a/src/systemd/src/basic/utf8.h +++ b/src/systemd/src/basic/utf8.h @@ -9,7 +9,7 @@ #endif /* NM_IGNORED */ #include "macro.h" -#include "missing.h" +#include "missing_type.h" #define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" #define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index ae8f0cad7c..382e95a072 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -25,6 +25,7 @@ #include "def.h" #include "device-nodes.h" #include "dirent-util.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index 2f3d1eeab8..f009d37d4c 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -25,7 +25,6 @@ #include "format-util.h" #include "macro.h" -#include "missing.h" #include "time-util.h" size_t page_size(void) _pure_; @@ -148,6 +147,12 @@ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { return memcmp(s1, s2, n); } +/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */ +static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) { + return memcmp_safe(s1, s2, MIN(n1, n2)) + ?: CMP(n1, n2); +} + int on_ac_power(void); #define memzero(x,l) \ @@ -173,7 +178,7 @@ static inline void _reset_errno_(int *saved_errno) { } #define PROTECT_ERRNO \ - _cleanup_(_reset_errno_) __attribute__((__unused__)) int _saved_errno_ = errno + _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno static inline int negative_errno(void) { /* This helper should be used to shut up gcc if you know 'errno' is @@ -194,7 +199,7 @@ static inline unsigned u64log2(uint64_t n) { static inline unsigned u32ctz(uint32_t n) { #if __SIZEOF_INT__ == 4 - return __builtin_ctz(n); + return n != 0 ? __builtin_ctz(n) : 32; #else #error "Wut?" #endif diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index e58045d6e9..c2627bbe6e 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -9,11 +9,11 @@ #include "in-addr-util.h" #include "lldp-internal.h" #include "lldp-neighbor.h" +#include "missing.h" #include "unaligned.h" +#include "util.h" -static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) { - const LLDPNeighborID *id = p; - +static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { siphash24_compress(id->chassis_id, id->chassis_id_size, state); siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); siphash24_compress(id->port_id, id->port_id_size, state); @@ -21,27 +21,12 @@ static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) { } int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { - int r; - - r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size)); - if (r != 0) - return r; - - r = CMP(x->chassis_id_size, y->chassis_id_size); - if (r != 0) - return r; - - r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size)); - if (r != 0) - return r; - - return CMP(x->port_id_size, y->port_id_size); + return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) + ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); } -const struct hash_ops lldp_neighbor_id_hash_ops = { - .hash = lldp_neighbor_id_hash_func, - .compare = (__compar_fn_t) lldp_neighbor_id_compare_func, -}; +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func, + sd_lldp_neighbor, lldp_neighbor_unlink); int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { const sd_lldp_neighbor *x = a, *y = b; @@ -99,7 +84,12 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { if (!n->lldp) return NULL; - assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n); + /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is + * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register + * ourselves from the hashtable and sometimes are called after we already are de-registered. */ + + (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n); + assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); n->lldp = NULL; diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h index 2241c3bd9d..62dbff42ca 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.h +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h @@ -80,7 +80,7 @@ static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; } -extern const struct hash_ops lldp_neighbor_id_hash_ops; +extern const struct hash_ops lldp_neighbor_hash_ops; int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y); int lldp_neighbor_prioq_compare_func(const void *a, const void *b); diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c index db779aaa81..c2af0368c1 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.c +++ b/src/systemd/src/libsystemd-network/lldp-network.c @@ -7,6 +7,7 @@ #include "fd-util.h" #include "lldp-network.h" +#include "missing.h" #include "socket-util.h" int lldp_network_bind_raw_socket(int ifindex) { diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index b2461b99e4..520f0783c3 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -374,36 +374,6 @@ int config_parse_hwaddrs(const char *unit, return 0; } -int config_parse_iaid(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - uint32_t iaid; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atou32(rvalue, &iaid); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Unable to read IAID, ignoring assignment: %s", rvalue); - return 0; - } - - *((uint32_t *)data) = iaid; - - return 0; -} - int config_parse_bridge_port_priority( const char *unit, const char *filename, diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 06d6118448..dfe4c4244b 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -36,7 +36,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); -CONFIG_PARSER_PROTOTYPE(config_parse_iaid); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); int net_get_unique_predictable_data(sd_device *device, uint64_t *result); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 6ca6bcab53..aac24767c8 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -25,10 +25,11 @@ #include "dns-domain.h" #include "event-util.h" #include "hostname-util.h" +#include "io-util.h" #include "random-util.h" #include "string-util.h" -#include "util.h" #include "strv.h" +#include "util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -343,8 +344,9 @@ int sd_dhcp_client_set_client_id( */ static int dhcp_client_set_iaid_duid_internal( sd_dhcp_client *client, + bool iaid_append, + bool iaid_set, uint32_t iaid, - bool append_iaid, uint16_t duid_type, const void *duid, size_t duid_len, @@ -366,17 +368,17 @@ static int dhcp_client_set_iaid_duid_internal( zero(client->client_id); client->client_id.type = 255; - if (append_iaid) { - /* If IAID is not configured, generate it. */ - if (iaid == 0) { + if (iaid_append) { + if (iaid_set) + client->client_id.ns.iaid = htobe32(iaid); + else { r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &client->client_id.ns.iaid); if (r < 0) return r; - } else - client->client_id.ns.iaid = htobe32(iaid); + } } if (duid != NULL) { @@ -416,10 +418,10 @@ static int dhcp_client_set_iaid_duid_internal( } client->client_id_len = sizeof(client->client_id.type) + len + - (append_iaid ? sizeof(client->client_id.ns.iaid) : 0); + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : ""); + log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : ""); client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); sd_dhcp_client_start(client); } @@ -429,18 +431,20 @@ static int dhcp_client_set_iaid_duid_internal( int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time); } int sd_dhcp_client_set_duid( @@ -448,13 +452,13 @@ int sd_dhcp_client_set_duid( uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_duid_llt( sd_dhcp_client *client, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time); } #endif /* NM_IGNORED */ @@ -1784,8 +1788,7 @@ static int client_receive_message_raw( if (!packet) return -ENOMEM; - iov.iov_base = packet; - iov.iov_len = buflen; + iov = IOVEC_MAKE(packet, buflen); len = recvmsg(fd, &msg, 0); if (len < 0) { diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index b953efc3df..b95ad0c978 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -18,6 +18,7 @@ #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" #include "dns-domain.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "hexdecoct.h" @@ -28,6 +29,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "tmpfile-util.h" #include "unaligned.h" int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { @@ -355,7 +357,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { return 0; } - r = dns_name_normalize(name, &normalized); + r = dns_name_normalize(name, 0, &normalized); if (r < 0) return r; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index 8c9fb932bc..63b7cf2b71 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -58,7 +58,7 @@ struct sd_dhcp6_client { struct sd_dhcp6_lease *lease; int fd; bool information_request; - bool has_iaid; + bool iaid_set; be16_t *req_opts; size_t req_opts_allocated; size_t req_opts_len; @@ -278,7 +278,7 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { client->ia_na.ia_na.id = htobe32(iaid); client->ia_pd.ia_pd.id = htobe32(iaid); - client->has_iaid = true; + client->iaid_set = true; return 0; } @@ -802,7 +802,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { assert(client); - if (client->has_iaid) + if (client->iaid_set) return 0; r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid); @@ -811,7 +811,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { client->ia_na.ia_na.id = iaid; client->ia_pd.ia_pd.id = iaid; - client->has_iaid = true; + client->iaid_set = true; return 0; } diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 6715f89420..72cb6e8bd5 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -4,6 +4,7 @@ #include <arpa/inet.h> #include <linux/sockios.h> +#include <sys/ioctl.h> #include "sd-lldp.h" @@ -29,12 +30,9 @@ static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event); static void lldp_flush_neighbors(sd_lldp *lldp) { - sd_lldp_neighbor *n; - assert(lldp); - while ((n = hashmap_first(lldp->neighbor_by_id))) - lldp_neighbor_unlink(n); + hashmap_clear(lldp->neighbor_by_id); } static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { @@ -377,7 +375,7 @@ _public_ int sd_lldp_new(sd_lldp **ret) { .capability_mask = (uint16_t) -1, }; - lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops); + lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); if (!lldp->neighbor_by_id) return -ENOMEM; diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 8e221ceedd..fc8c5148ee 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -1375,8 +1375,7 @@ static int event_make_inotify_data( return 1; } -static int inode_data_compare(const void *a, const void *b) { - const struct inode_data *x = a, *y = b; +static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) { int r; assert(x); @@ -1389,19 +1388,14 @@ static int inode_data_compare(const void *a, const void *b) { return CMP(x->ino, y->ino); } -static void inode_data_hash_func(const void *p, struct siphash *state) { - const struct inode_data *d = p; - - assert(p); +static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) { + assert(d); siphash24_compress(&d->dev, sizeof(d->dev), state); siphash24_compress(&d->ino, sizeof(d->ino), state); } -const struct hash_ops inode_data_hash_ops = { - .hash = inode_data_hash_func, - .compare = inode_data_compare -}; +DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare); static void event_free_inode_data( sd_event *e, diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c index d4d668e885..1e654f0ea4 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -187,16 +187,13 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { return id128_write_fd(fd, f, id, do_sync); } -void id128_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, 16, state); +void id128_hash_func(const sd_id128_t *p, struct siphash *state) { + siphash24_compress(p, sizeof(sd_id128_t), state); } -int id128_compare_func(const void *a, const void *b) { +int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) { return memcmp(a, b, 16); } -const struct hash_ops id128_hash_ops = { - .hash = id128_hash_func, - .compare = id128_compare_func, -}; +DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); #endif /* NM_IGNORED */ diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h index 44f159c0b6..65f14ab252 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.h +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h @@ -28,6 +28,6 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret); int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); -void id128_hash_func(const void *p, struct siphash *state); -int id128_compare_func(const void *a, const void *b) _pure_; +void id128_hash_func(const sd_id128_t *p, struct siphash *state); +int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index bede1db6d5..ccf6298eea 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -21,6 +21,7 @@ #include "dns-domain.h" #include "hashmap.h" #include "hexdecoct.h" +#include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" @@ -28,9 +29,9 @@ #include "strv.h" #include "utf8.h" -int dns_label_unescape(const char **name, char *dest, size_t sz) { +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) { const char *n; - char *d; + char *d, last_char = 0; int r = 0; assert(name); @@ -40,13 +41,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { d = dest; for (;;) { - if (*n == '.') { - n++; - break; - } + if (*n == 0 || *n == '.') { + if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') + /* Trailing dash */ + return -EINVAL; - if (*n == 0) + if (*n == '.') + n++; break; + } if (r >= DNS_LABEL_MAX) return -EINVAL; @@ -56,6 +59,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (*n == '\\') { /* Escaped character */ + if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES)) + return -EINVAL; n++; @@ -66,6 +71,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { else if (IN_SET(*n, '\\', '.')) { /* Escaped backslash or dot */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) + return -EINVAL; + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -94,6 +103,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (k > 255) return -EINVAL; + if (FLAGS_SET(flags, DNS_LABEL_LDH) && + !valid_ldh_char((char) k)) + return -EINVAL; + + last_char = (char) k; if (d) *(d++) = (char) k; sz--; @@ -107,6 +121,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { /* Normal character */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) { + if (!valid_ldh_char(*n)) + return -EINVAL; + if (r == 0 && *n == '-') + /* Leading dash */ + return -EINVAL; + } + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -189,7 +212,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha terminal--; } - r = dns_label_unescape(&name, dest, sz); + r = dns_label_unescape(&name, dest, sz, 0); if (r < 0) return r; @@ -386,7 +409,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, #endif #endif /* NM_IGNORED */ -int dns_name_concat(const char *a, const char *b, char **_ret) { +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) { _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; const char *p; @@ -403,7 +426,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, flags); if (r < 0) return r; if (r == 0) { @@ -469,8 +492,7 @@ finish: } #if 0 /* NM_IGNORED */ -void dns_name_hash_func(const void *s, struct siphash *state) { - const char *p = s; +void dns_name_hash_func(const char *p, struct siphash *state) { int r; assert(p); @@ -478,7 +500,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) { for (;;) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) break; if (r == 0) @@ -493,15 +515,15 @@ void dns_name_hash_func(const void *s, struct siphash *state) { string_hash_func("", state); } -int dns_name_compare_func(const void *a, const void *b) { +int dns_name_compare_func(const char *a, const char *b) { const char *x, *y; int r, q; assert(a); assert(b); - x = (const char *) a + strlen(a); - y = (const char *) b + strlen(b); + x = a + strlen(a); + y = b + strlen(b); for (;;) { char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; @@ -520,10 +542,7 @@ int dns_name_compare_func(const void *a, const void *b) { } } -const struct hash_ops dns_name_hash_ops = { - .hash = dns_name_hash_func, - .compare = dns_name_compare_func -}; +DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func); int dns_name_equal(const char *x, const char *y) { int r, q; @@ -534,11 +553,11 @@ int dns_name_equal(const char *x, const char *y) { for (;;) { char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -565,14 +584,14 @@ int dns_name_endswith(const char *name, const char *suffix) { for (;;) { char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_n) saved_n = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -603,13 +622,13 @@ int dns_name_startswith(const char *name, const char *prefix) { for (;;) { char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, lp, sizeof(lp)); + r = dns_label_unescape(&p, lp, sizeof lp, 0); if (r < 0) return r; if (r == 0) return true; - q = dns_label_unescape(&n, ln, sizeof(ln)); + q = dns_label_unescape(&n, ln, sizeof ln, 0); if (q < 0) return q; @@ -638,14 +657,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char if (!saved_before) saved_before = n; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_after) saved_after = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -668,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char /* Found it! Now generate the new name */ prefix = strndupa(name, saved_before - name); - r = dns_name_concat(prefix, new_suffix, ret); + r = dns_name_concat(prefix, new_suffix, 0, ret); if (r < 0) return r; @@ -746,7 +765,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { for (i = 0; i < ELEMENTSOF(a); i++) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) return r; if (r == 0) @@ -783,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { char label[DNS_LABEL_MAX+1]; int x, y; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -792,7 +811,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { if (x < 0) return -EINVAL; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -861,7 +880,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo * dns_label_unescape() returns 0 when it hits the end * of the domain name, which we rely on here to encode * the trailing NUL byte. */ - r = dns_label_unescape(&domain, (char *) out, len); + r = dns_label_unescape(&domain, (char *) out, len, 0); if (r < 0) return r; @@ -929,7 +948,7 @@ bool dns_srv_type_is_valid(const char *name) { /* This more or less implements RFC 6335, Section 5.1 */ - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return false; if (r == 0) @@ -989,7 +1008,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha return -EINVAL; if (!name) - return dns_name_concat(type, domain, ret); + return dns_name_concat(type, domain, 0, ret); if (!dns_service_name_is_valid(name)) return -EINVAL; @@ -998,11 +1017,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha if (r < 0) return r; - r = dns_name_concat(type, domain, &n); + r = dns_name_concat(type, domain, 0, &n); if (r < 0) return r; - return dns_name_concat(escaped, n, ret); + return dns_name_concat(escaped, n, 0, ret); } static bool dns_service_name_label_is_valid(const char *label, size_t n) { @@ -1027,7 +1046,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do assert(joined); /* Get first label from the full name */ - an = dns_label_unescape(&p, a, sizeof(a)); + an = dns_label_unescape(&p, a, sizeof(a), 0); if (an < 0) return an; @@ -1035,7 +1054,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do x++; /* If there was a first label, try to get the second one */ - bn = dns_label_unescape(&p, b, sizeof(b)); + bn = dns_label_unescape(&p, b, sizeof(b), 0); if (bn < 0) return bn; @@ -1044,7 +1063,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do /* If there was a second label, try to get the third one */ q = p; - cn = dns_label_unescape(&p, c, sizeof(c)); + cn = dns_label_unescape(&p, c, sizeof(c), 0); if (cn < 0) return cn; @@ -1094,7 +1113,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do d = joined; finish: - r = dns_name_normalize(d, &domain); + r = dns_name_normalize(d, 0, &domain); if (r < 0) return r; @@ -1110,7 +1129,7 @@ finish: return 0; } -static int dns_name_build_suffix_table(const char *name, const char*table[]) { +static int dns_name_build_suffix_table(const char *name, const char *table[]) { const char *p; unsigned n = 0; int r; @@ -1239,12 +1258,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { } x = a_labels[n - 1 - k]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; y = b_labels[m - 1 - k]; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -1312,13 +1331,13 @@ int dns_name_apply_idna(const char *name, char **ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return r; if (r == 0) break; - q = dns_label_apply_idna(label, r, label, sizeof(label)); + q = dns_label_apply_idna(label, r, label, sizeof label); if (q < 0) return q; if (q > 0) diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index 95f4069cc0..88b3eb112c 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -24,13 +24,18 @@ /* Maximum number of labels per valid hostname */ #define DNS_N_LABELS_MAX 127 -int dns_label_unescape(const char **name, char *dest, size_t sz); +typedef enum DNSLabelFlags { + DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */ + DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */ +} DNSLabelFlags; + +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags); int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); int dns_label_escape_new(const char *p, size_t l, char **ret); static inline int dns_name_parent(const char **name) { - return dns_label_unescape(name, NULL, DNS_LABEL_MAX); + return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0); } #if 0 /* NM_IGNORED */ @@ -40,18 +45,29 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, #endif #endif /* NM_IGNORED */ -int dns_name_concat(const char *a, const char *b, char **ret); +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret); -static inline int dns_name_normalize(const char *s, char **ret) { +static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) { /* dns_name_concat() normalizes as a side-effect */ - return dns_name_concat(s, NULL, ret); + return dns_name_concat(s, NULL, flags, ret); } static inline int dns_name_is_valid(const char *s) { int r; /* dns_name_normalize() verifies as a side effect */ - r = dns_name_normalize(s, NULL); + r = dns_name_normalize(s, 0, NULL); + if (r == -EINVAL) + return 0; + if (r < 0) + return r; + return 1; +} + +static inline int dns_name_is_valid_ldh(const char *s) { + int r; + + r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL); if (r == -EINVAL) return 0; if (r < 0) @@ -59,8 +75,8 @@ static inline int dns_name_is_valid(const char *s) { return 1; } -void dns_name_hash_func(const void *s, struct siphash *state); -int dns_name_compare_func(const void *a, const void *b); +void dns_name_hash_func(const char *s, struct siphash *state); +int dns_name_compare_func(const char *a, const char *b); extern const struct hash_ops dns_name_hash_ops; int dns_name_between(const char *a, const char *b, const char *c); diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h index b026b5c551..05c38008cf 100644 --- a/src/systemd/src/systemd/_sd-common.h +++ b/src/systemd/src/systemd/_sd-common.h @@ -23,9 +23,11 @@ # error "Do not include _sd-common.h directly; it is a private header." #endif +typedef void (*_sd_destroy_t)(void *userdata); + #ifndef _sd_printf_ # if __GNUC__ >= 4 -# define _sd_printf_(a,b) __attribute__ ((__format__(printf, a, b))) +# define _sd_printf_(a,b) __attribute__((__format__(printf, a, b))) # else # define _sd_printf_(a,b) # endif diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index e388552064..bd0d429df6 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -23,6 +23,7 @@ #include <net/ethernet.h> #include <netinet/in.h> #include <sys/types.h> +#include <stdbool.h> #include "sd-dhcp-lease.h" #include "sd-event.h" @@ -127,12 +128,14 @@ int sd_dhcp_client_set_client_id( size_t data_len); int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len); int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint64_t llt_time); int sd_dhcp_client_set_duid( diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index b15cade20a..787a12f241 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -77,7 +77,7 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, typedef void* sd_event_child_handler_t; #endif typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata); -typedef void (*sd_event_destroy_t)(void *userdata); +typedef _sd_destroy_t sd_event_destroy_t; int sd_event_default(sd_event **e); diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h index 78cf9462b0..f4c05a3683 100644 --- a/src/systemd/src/systemd/sd-id128.h +++ b/src/systemd/src/systemd/sd-id128.h @@ -41,19 +41,20 @@ int sd_id128_from_string(const char *s, sd_id128_t *ret); int sd_id128_randomize(sd_id128_t *ret); int sd_id128_get_machine(sd_id128_t *ret); -int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); -int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); int sd_id128_get_boot(sd_id128_t *ret); int sd_id128_get_invocation(sd_id128_t *ret); -#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ - ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ - 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) +int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); +int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); #define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }} +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + + /* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 * times. It is hence not a good idea to call this macro with an * expensive function as parameter or an expression with side @@ -109,7 +110,12 @@ _sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { return a.qwords[0] == 0 && a.qwords[1] == 0; } +_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) { + return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF); +} + #define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) +#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }}) _SD_END_DECLARATIONS; |