diff options
| author | Michael Biebl <biebl@debian.org> | 2022-02-14 19:23:28 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2022-02-14 19:23:28 +0100 |
| commit | 2f94dba7385fd0e0ef19a06eb4a2fcf6c43d7946 (patch) | |
| tree | e2222f5577115985dd52044d2991253403cdd952 /src/libnm-systemd-shared | |
| parent | 88c227d90a6b7b388c5c85d72802a0ca8f05ed5c (diff) | |
New upstream version 1.35.91 upstream/1.35.91
Diffstat (limited to 'src/libnm-systemd-shared')
58 files changed, 1472 insertions, 768 deletions
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/filesystems.h b/src/libnm-systemd-shared/sd-adapt-shared/filesystems.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/libnm-systemd-shared/sd-adapt-shared/filesystems.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/libnm-systemd-shared/sd-adapt-shared/hmac.h b/src/libnm-systemd-shared/sd-adapt-shared/hmac.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/libnm-systemd-shared/sd-adapt-shared/hmac.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h b/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h index 637892c2..a5dce206 100644 --- a/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h +++ b/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h @@ -1,3 +1,5 @@ #pragma once /* dummy header */ + +#include <linux/magic.h> diff --git a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h b/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h index e587fe79..65d51756 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -22,20 +22,25 @@ typedef void (*free_func_t)(void *p); #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t))) +#define alloca_safe(n) \ + ({ \ + size_t _nn_ = n; \ + assert(_nn_ <= ALLOCA_MAX); \ + alloca(_nn_ == 0 ? 1 : _nn_); \ + }) \ + #define newa(t, n) \ ({ \ size_t _n_ = n; \ assert(!size_multiply_overflow(sizeof(t), _n_)); \ - assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ - (t*) alloca((sizeof(t)*_n_) ?: 1); \ + (t*) alloca_safe(sizeof(t)*_n_); \ }) #define newa0(t, n) \ ({ \ size_t _n_ = n; \ assert(!size_multiply_overflow(sizeof(t), _n_)); \ - assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ - (t*) alloca0((sizeof(t)*_n_) ?: 1); \ + (t*) alloca0((sizeof(t)*_n_)); \ }) #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) @@ -44,12 +49,6 @@ typedef void (*free_func_t)(void *p); #define malloc0(n) (calloc(1, (n) ?: 1)) -#define mfree(memory) \ - ({ \ - free(memory); \ - (typeof(memory)) NULL; \ - }) - #define free_and_replace(a, b) \ ({ \ typeof(a)* _a = &(a); \ @@ -67,8 +66,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s ({ \ void *_q_; \ size_t _l_ = l; \ - assert(_l_ <= ALLOCA_MAX); \ - _q_ = alloca(_l_ ?: 1); \ + _q_ = alloca_safe(_l_); \ memcpy_safe(_q_, p, _l_); \ }) @@ -76,8 +74,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s ({ \ void *_q_; \ size_t _l_ = l; \ - assert(_l_ <= ALLOCA_MAX); \ - _q_ = alloca(_l_ + 1); \ + _q_ = alloca_safe(_l_ + 1); \ ((uint8_t*) _q_)[_l_] = 0; \ memcpy_safe(_q_, p, _l_); \ }) @@ -144,9 +141,8 @@ void* greedy_realloc0(void **p, size_t need, size_t size); ({ \ char *_new_; \ size_t _len_ = n; \ - assert(_len_ <= ALLOCA_MAX); \ - _new_ = alloca(_len_ ?: 1); \ - (void *) memset(_new_, 0, _len_); \ + _new_ = alloca_safe(_len_); \ + memset(_new_, 0, _len_); \ }) /* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */ @@ -155,8 +151,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size); void *_ptr_; \ size_t _mask_ = (align) - 1; \ size_t _size_ = size; \ - assert(_size_ <= ALLOCA_MAX); \ - _ptr_ = alloca((_size_ + _mask_) ?: 1); \ + _ptr_ = alloca_safe(_size_ + _mask_); \ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ }) @@ -165,7 +160,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size); void *_new_; \ size_t _xsize_ = (size); \ _new_ = alloca_align(_xsize_, (align)); \ - (void*)memset(_new_, 0, _xsize_); \ + memset(_new_, 0, _xsize_); \ }) #if HAS_FEATURE_MEMORY_SANITIZER @@ -193,3 +188,19 @@ void* greedy_realloc0(void **p, size_t need, size_t size); __builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \ VOID_0)) + + +/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */ +#define strdupa_safe(s) \ + ({ \ + const char *_t = (s); \ + (char*) memdupa_suffix0(_t, strlen(_t)); \ + }) + +#define strndupa_safe(s, n) \ + ({ \ + const char *_t = (s); \ + (char*) memdupa_suffix0(_t, strnlen(_t, (n))); \ + }) + +#include "memory-util.h" diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index 90ccd2c0..461c01b3 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -2,6 +2,7 @@ #pragma once #include <dirent.h> +#include <fcntl.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> @@ -33,6 +34,9 @@ typedef enum CGroupController { CGROUP_CONTROLLER_BPF_FOREIGN, CGROUP_CONTROLLER_BPF_SOCKET_BIND, CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES, + /* The BPF hook implementing RestrictFileSystems= is not defined here. + * It's applied as late as possible in exec_child() so we don't block + * our own unit setup code. */ _CGROUP_CONTROLLER_MAX, _CGROUP_CONTROLLER_INVALID = -EINVAL, @@ -123,6 +127,20 @@ static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); } +/* Special values for the special {blkio,io}.bfq.weight attribute */ +#define CGROUP_BFQ_WEIGHT_INVALID UINT64_MAX +#define CGROUP_BFQ_WEIGHT_MIN UINT64_C(1) +#define CGROUP_BFQ_WEIGHT_MAX UINT64_C(1000) +#define CGROUP_BFQ_WEIGHT_DEFAULT UINT64_C(100) + +/* Convert the normal io.weight value to io.bfq.weight */ +static inline uint64_t BFQ_WEIGHT(uint64_t io_weight) { + return + io_weight <= CGROUP_WEIGHT_DEFAULT ? + CGROUP_BFQ_WEIGHT_DEFAULT - (CGROUP_WEIGHT_DEFAULT - io_weight) * (CGROUP_BFQ_WEIGHT_DEFAULT - CGROUP_BFQ_WEIGHT_MIN) / (CGROUP_WEIGHT_DEFAULT - CGROUP_WEIGHT_MIN) : + CGROUP_BFQ_WEIGHT_DEFAULT + (io_weight - CGROUP_WEIGHT_DEFAULT) * (CGROUP_BFQ_WEIGHT_MAX - CGROUP_BFQ_WEIGHT_DEFAULT) / (CGROUP_WEIGHT_MAX - CGROUP_WEIGHT_DEFAULT); +} + /* Special values for the blkio.weight attribute */ #define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX #define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) @@ -236,6 +254,7 @@ int cg_is_empty_recursive(const char *controller, const char *path); int cg_get_root_path(char **path); +int cg_path_get_cgroupid(const char *path, uint64_t *ret); int cg_path_get_session(const char *path, char **session); int cg_path_get_owner_uid(const char *path, uid_t *uid); int cg_path_get_unit(const char *path, char **unit); @@ -311,3 +330,12 @@ typedef enum ManagedOOMPreference { const char* managed_oom_preference_to_string(ManagedOOMPreference a) _const_; ManagedOOMPreference managed_oom_preference_from_string(const char *s) _pure_; + +/* The structure to pass to name_to_handle_at() on cgroupfs2 */ +typedef union { + struct file_handle file_handle; + uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)]; +} cg_file_handle; + +#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) } +#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle) diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index 431862f6..f47ddefe 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "env-util.h" +#include "errno-util.h" #include "escape.h" #include "extract-word.h" #include "macro.h" @@ -397,7 +398,7 @@ int strv_env_replace_consume(char ***l, char *p) { return -EINVAL; } - name = strndupa(p, t - p); + name = strndupa_safe(p, t - p); STRV_FOREACH(f, *l) if (env_entry_has_name(*f, name)) { @@ -484,7 +485,7 @@ char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { if (flags & REPLACE_ENV_USE_ENVIRONMENT) { const char *t; - t = strndupa(name, k); + t = strndupa_safe(name, k); return getenv(t); }; @@ -791,15 +792,12 @@ int getenv_bool_secure(const char *p) { #if 0 /* NM_IGNORED */ int set_unset_env(const char *name, const char *value, bool overwrite) { - int r; + assert(name); if (value) - r = setenv(name, value, overwrite); - else - r = unsetenv(name); - if (r < 0) - return -errno; - return 0; + return RET_NERRNO(setenv(name, value, overwrite)); + + return RET_NERRNO(unsetenv(name)); } int putenv_dup(const char *assignment, bool override) { @@ -809,12 +807,10 @@ int putenv_dup(const char *assignment, bool override) { if (!e) return -EINVAL; - n = strndupa(assignment, e - assignment); + n = strndupa_safe(assignment, e - assignment); /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */ - if (setenv(n, e + 1, override) < 0) - return -errno; - return 0; + return RET_NERRNO(setenv(n, e + 1, override)); } int setenv_systemd_exec_pid(bool update_only) { diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h index 3f2d0af5..09abf0b7 100644 --- a/src/libnm-systemd-shared/src/basic/errno-util.h +++ b/src/libnm-systemd-shared/src/basic/errno-util.h @@ -13,7 +13,7 @@ static inline void _reset_errno_(int *saved_errno) { errno = *saved_errno; } -#define PROTECT_ERRNO \ +#define PROTECT_ERRNO \ _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno #define UNPROTECT_ERRNO \ @@ -31,6 +31,29 @@ static inline int negative_errno(void) { return -errno; } +static inline int RET_NERRNO(int ret) { + + /* Helper to wrap system calls in to make them return negative errno errors. This brings system call + * error handling in sync with how we usually handle errors in our own code, i.e. with immediate + * returning of negative errno. Usage is like this: + * + * … + * r = RET_NERRNO(unlink(t)); + * … + * + * or + * + * … + * fd = RET_NERRNO(open("/etc/fstab", O_RDONLY|O_CLOEXEC)); + * … + */ + + if (ret < 0) + return negative_errno(); + + return ret; +} + static inline const char *strerror_safe(int error) { /* 'safe' here does NOT mean thread safety. */ return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */ @@ -47,6 +70,13 @@ static inline int errno_or_else(int fallback) { return -abs(fallback); } +/* For send()/recv() or read()/write(). */ +static inline bool ERRNO_IS_TRANSIENT(int r) { + return IN_SET(abs(r), + EAGAIN, + EINTR); +} + /* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5. * * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the @@ -77,10 +107,8 @@ static inline bool ERRNO_IS_DISCONNECT(int r) { * the accept(2) man page. */ static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) { return ERRNO_IS_DISCONNECT(r) || - IN_SET(abs(r), - EAGAIN, - EINTR, - EOPNOTSUPP); + ERRNO_IS_TRANSIENT(r) || + abs(r) == EOPNOTSUPP; } /* Resource exhaustion, could be our fault or general system trouble */ diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c index 93288563..4dfb9a16 100644 --- a/src/libnm-systemd-shared/src/basic/escape.c +++ b/src/libnm-systemd-shared/src/basic/escape.c @@ -547,7 +547,7 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) { return str_realloc(buf); } -char* quote_command_line(char **argv) { +char* quote_command_line(char **argv, ShellEscapeFlags flags) { _cleanup_free_ char *result = NULL; assert(argv); @@ -556,7 +556,7 @@ char* quote_command_line(char **argv) { STRV_FOREACH(a, argv) { _cleanup_free_ char *t = NULL; - t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY); + t = shell_maybe_quote(*a, flags); if (!t) return NULL; @@ -564,6 +564,6 @@ char* quote_command_line(char **argv) { return NULL; } - return TAKE_PTR(result); + return str_realloc(TAKE_PTR(result)); } #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h index d490510d..318da6f2 100644 --- a/src/libnm-systemd-shared/src/basic/escape.h +++ b/src/libnm-systemd-shared/src/basic/escape.h @@ -69,4 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl char* shell_escape(const char *s, const char *bad); char* shell_maybe_quote(const char *s, ShellEscapeFlags flags); -char* quote_command_line(char **argv); +char* quote_command_line(char **argv, ShellEscapeFlags flags); diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c index c73dcff1..4504b6be 100644 --- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c @@ -9,20 +9,29 @@ #include <sys/types.h> #include "ether-addr-util.h" +#include "hexdecoct.h" #include "macro.h" #include "string-util.h" -char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) { +char *hw_addr_to_string_full( + const struct hw_addr_data *addr, + HardwareAddressToStringFlags flags, + char buffer[static HW_ADDR_TO_STRING_MAX]) { + assert(addr); assert(buffer); assert(addr->length <= HW_ADDR_MAX_SIZE); - for (size_t i = 0; i < addr->length; i++) { - sprintf(&buffer[3*i], "%02"PRIx8, addr->bytes[i]); - if (i < addr->length - 1) - buffer[3*i + 2] = ':'; + for (size_t i = 0, j = 0; i < addr->length; i++) { + buffer[j++] = hexchar(addr->bytes[i] >> 4); + buffer[j++] = hexchar(addr->bytes[i] & 0x0f); + if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON)) + buffer[j++] = ':'; } + buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ? + addr->length * 2 : + addr->length * 3 - 1] = '\0'; return buffer; } @@ -39,7 +48,7 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) return memcmp(a->bytes, b->bytes, a->length); } -static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) { +void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) { assert(p); assert(state); @@ -48,6 +57,7 @@ static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *stat } DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free); char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { assert(addr); @@ -93,75 +103,163 @@ static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *sta } DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free); -int ether_addr_from_string(const char *s, struct ether_addr *ret) { - size_t pos = 0, n, field; - char sep = '\0'; - const char *hex = HEXDIGITS, *hexoff; - size_t x; - bool touched; - -#define parse_fields(v) \ - for (field = 0; field < ELEMENTSOF(v); field++) { \ - touched = false; \ - for (n = 0; n < (2 * sizeof(v[0])); n++) { \ - if (s[pos] == '\0') \ - break; \ - hexoff = strchr(hex, s[pos]); \ - if (!hexoff) \ - break; \ - assert(hexoff >= hex); \ - x = hexoff - hex; \ - if (x >= 16) \ - x -= 6; /* A-F */ \ - assert(x < 16); \ - touched = true; \ - v[field] <<= 4; \ - v[field] += x; \ - pos++; \ - } \ - if (!touched) \ - return -EINVAL; \ - if (field < (ELEMENTSOF(v)-1)) { \ - if (s[pos] != sep) \ - return -EINVAL; \ - else \ - pos++; \ - } \ - } +static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) { + const char *hex = HEXDIGITS, *p; + uint16_t data = 0; + bool cont; assert(s); - assert(ret); + assert(*s); + assert(IN_SET(len, 1, 2)); + assert(buf); - s += strspn(s, WHITESPACE); - sep = s[strspn(s, hex)]; + p = *s; - if (sep == '.') { - uint16_t shorts[3] = { 0 }; + for (size_t i = 0; i < len * 2; i++) { + const char *hexoff; + size_t x; - parse_fields(shorts); + if (*p == '\0' || *p == sep) { + if (i == 0) + return -EINVAL; + break; + } - if (s[pos] != '\0') + hexoff = strchr(hex, *p); + if (!hexoff) return -EINVAL; - for (n = 0; n < ELEMENTSOF(shorts); n++) { - ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); - ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); + assert(hexoff >= hex); + x = hexoff - hex; + if (x >= 16) + x -= 6; /* A-F */ + + assert(x < 16); + data <<= 4; + data += x; + + p++; + } + + if (*p != '\0' && *p != sep) + return -EINVAL; + + switch (len) { + case 1: + buf[0] = data; + break; + case 2: + buf[0] = (data & 0xff00) >> 8; + buf[1] = data & 0xff; + break; + default: + assert_not_reached(); + } + + cont = *p == sep; + *s = p + cont; + return cont; +} + +int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) { + size_t field_size, max_len, len = 0; + uint8_t bytes[HW_ADDR_MAX_SIZE]; + char sep; + int r; + + assert(s); + assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX); + assert(ret); + + /* This accepts the following formats: + * + * Dot separated 2 bytes format: xxyy.zzaa.bbcc + * Colon separated 1 bytes format: xx:yy:zz:aa:bb:cc + * Hyphen separated 1 bytes format: xx-yy-zz-aa-bb-cc + * + * Moreover, if expected_len == 0, 4, or 16, this also accepts: + * + * IPv4 format: used by IPv4 tunnel, e.g. ipgre + * IPv6 format: used by IPv6 tunnel, e.g. ip6gre + * + * The expected_len argument controls the length of acceptable addresses: + * + * 0: accepts 4 (AF_INET), 16 (AF_INET6), 6 (ETH_ALEN), or 20 (INFINIBAND_ALEN). + * SIZE_MAX: accepts arbitrary length, but at least one separator must be included. + * Otherwise: accepts addresses with matching length. + */ + + if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) { + union in_addr_union a; + int family; + + if (expected_len == 0) + r = in_addr_from_string_auto(s, &family, &a); + else { + family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6; + r = in_addr_from_string(family, s, &a); + } + if (r >= 0) { + ret->length = FAMILY_ADDRESS_SIZE(family); + memcpy(ret->bytes, a.bytes, ret->length); + return 0; } + } - } else if (IN_SET(sep, ':', '-')) { - struct ether_addr out = ETHER_ADDR_NULL; + max_len = + expected_len == 0 ? INFINIBAND_ALEN : + expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len; + sep = s[strspn(s, HEXDIGITS)]; - parse_fields(out.ether_addr_octet); + if (sep == '.') + field_size = 2; + else if (IN_SET(sep, ':', '-')) + field_size = 1; + else + return -EINVAL; - if (s[pos] != '\0') - return -EINVAL; + if (max_len % field_size != 0) + return -EINVAL; - for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) - ret->ether_addr_octet[n] = out.ether_addr_octet[n]; + for (size_t i = 0; i < max_len / field_size; i++) { + r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size); + if (r < 0) + return r; + if (r == 0) { + len = (i + 1) * field_size; + break; + } + } - } else + if (len == 0) return -EINVAL; + if (expected_len == 0) { + if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN)) + return -EINVAL; + } else if (expected_len != SIZE_MAX) { + if (len != expected_len) + return -EINVAL; + } + + ret->length = len; + memcpy(ret->bytes, bytes, ret->length); + return 0; +} + +int parse_ether_addr(const char *s, struct ether_addr *ret) { + struct hw_addr_data a; + int r; + + assert(s); + assert(ret); + + r = parse_hw_addr_full(s, ETH_ALEN, &a); + if (r < 0) + return r; + + *ret = a.ether; return 0; } diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h index 794fc55b..32f45fe8 100644 --- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h @@ -6,6 +6,9 @@ #include <stdbool.h> #include "hash-funcs.h" +#include "in-addr-util.h" +#include "macro.h" +#include "memory-util.h" /* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h * defines a macro of the same name with a much lower size. */ @@ -16,29 +19,51 @@ struct hw_addr_data { union { struct ether_addr ether; uint8_t infiniband[INFINIBAND_ALEN]; + struct in_addr in; + struct in6_addr in6; uint8_t bytes[HW_ADDR_MAX_SIZE]; }; }; +int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret); +static inline int parse_hw_addr(const char *s, struct hw_addr_data *ret) { + return parse_hw_addr_full(s, 0, ret); +} +int parse_ether_addr(const char *s, struct ether_addr *ret); + +typedef enum HardwareAddressToStringFlags { + HW_ADDR_TO_STRING_NO_COLON = 1 << 0, +} HardwareAddressToStringFlags; + #define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE) -char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]); +char *hw_addr_to_string_full( + const struct hw_addr_data *addr, + HardwareAddressToStringFlags flags, + char buffer[static HW_ADDR_TO_STRING_MAX]); +static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buffer[static HW_ADDR_TO_STRING_MAX]) { + return hw_addr_to_string_full(addr, 0, buffer); +} /* Note: the lifetime of the compound literal is the immediately surrounding block, * see C11 §6.5.2.5, and * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */ -#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){}) +#define HW_ADDR_TO_STR_FULL(hw_addr, flags) hw_addr_to_string_full((hw_addr), flags, (char[HW_ADDR_TO_STRING_MAX]){}) +#define HW_ADDR_TO_STR(hw_addr) HW_ADDR_TO_STR_FULL(hw_addr, 0) #define HW_ADDR_NULL ((const struct hw_addr_data){}) +void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state); int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b); static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) { return hw_addr_compare(a, b) == 0; } static inline bool hw_addr_is_null(const struct hw_addr_data *addr) { - return hw_addr_equal(addr, &HW_ADDR_NULL); + assert(addr); + return addr->length == 0 || memeqzero(addr->bytes, addr->length); } extern const struct hash_ops hw_addr_hash_ops; +extern const struct hash_ops hw_addr_hash_ops_free; #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] @@ -60,6 +85,29 @@ static inline bool ether_addr_is_null(const struct ether_addr *addr) { return ether_addr_equal(addr, ÐER_ADDR_NULL); } -int ether_addr_from_string(const char *s, struct ether_addr *ret); +static inline bool ether_addr_is_broadcast(const struct ether_addr *addr) { + assert(addr); + return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN); +} + +static inline bool ether_addr_is_multicast(const struct ether_addr *addr) { + assert(addr); + return FLAGS_SET(addr->ether_addr_octet[0], 0x01); +} + +static inline bool ether_addr_is_unicast(const struct ether_addr *addr) { + return !ether_addr_is_multicast(addr); +} + +static inline bool ether_addr_is_local(const struct ether_addr *addr) { + /* Determine if the Ethernet address is locally-assigned one (IEEE 802) */ + assert(addr); + return FLAGS_SET(addr->ether_addr_octet[0], 0x02); +} + +static inline bool ether_addr_is_global(const struct ether_addr *addr) { + return !ether_addr_is_local(addr); +} extern const struct hash_ops ether_addr_hash_ops; +extern const struct hash_ops ether_addr_hash_ops_free; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index ee2a4caf..c3df68de 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -154,10 +154,7 @@ int fd_nonblock(int fd, bool nonblock) { if (nflags == flags) return 0; - if (fcntl(fd, F_SETFL, nflags) < 0) - return -errno; - - return 0; + return RET_NERRNO(fcntl(fd, F_SETFL, nflags)); } int fd_cloexec(int fd, bool cloexec) { @@ -173,10 +170,7 @@ int fd_cloexec(int fd, bool cloexec) { if (nflags == flags) return 0; - if (fcntl(fd, F_SETFD, nflags) < 0) - return -errno; - - return 0; + return RET_NERRNO(fcntl(fd, F_SETFD, nflags)); } #if 0 /* NM_IGNORED */ @@ -190,7 +184,7 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { return false; } -static int get_max_fd(void) { +int get_max_fd(void) { struct rlimit rl; rlim_t m; @@ -211,37 +205,147 @@ static int get_max_fd(void) { return (int) (m - 1); } -int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) { - static bool have_close_range = true; /* Assume we live in the future */ +static int close_all_fds_frugal(const int except[], size_t n_except) { + int max_fd, r = 0; + + assert(n_except == 0 || except); + + /* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so + * and hence is safe to be called in signal handler context. Most users should call close_all_fds(), + * but when we assume we are called from signal handler context, then use this simpler call + * instead. */ + + max_fd = get_max_fd(); + if (max_fd < 0) + return max_fd; + + /* Refuse to do the loop over more too many elements. It's better to fail immediately than to + * spin the CPU for a long time. */ + if (max_fd > MAX_FD_LOOP_LIMIT) + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "Refusing to loop over %d potential fds.", + max_fd); + + for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { + int q; + + if (fd_in_set(fd, except, n_except)) + continue; + + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) + r = q; + } + + return r; +} + +static bool have_close_range = true; /* Assume we live in the future */ + +static int close_all_fds_special_case(const int except[], size_t n_except) { + assert(n_except == 0 || except); + + /* Handles a few common special cases separately, since they are common and can be optimized really + * nicely, since we won't need sorting for them. Returns > 0 if the special casing worked, 0 + * otherwise. */ + + if (!have_close_range) + return 0; + + switch (n_except) { + + case 0: + /* Close everything. Yay! */ + + if (close_range(3, -1, 0) >= 0) + return 1; + + if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) { + have_close_range = false; + return 0; + } + + return -errno; + + case 1: + /* Close all but exactly one, then we don't need no sorting. This is a pretty common + * case, hence let's handle it specially. */ + + if ((except[0] <= 3 || close_range(3, except[0]-1, 0) >= 0) && + (except[0] >= INT_MAX || close_range(MAX(3, except[0]+1), -1, 0) >= 0)) + return 1; + + if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) { + have_close_range = false; + return 0; + } + + return -errno; + + default: + return 0; + } +} + +int close_all_fds_without_malloc(const int except[], size_t n_except) { + int r; + + assert(n_except == 0 || except); + + r = close_all_fds_special_case(except, n_except); + if (r < 0) + return r; + if (r > 0) /* special case worked! */ + return 0; + + return close_all_fds_frugal(except, n_except); +} + +int close_all_fds(const int except[], size_t n_except) { _cleanup_closedir_ DIR *d = NULL; int r = 0; assert(n_except == 0 || except); + r = close_all_fds_special_case(except, n_except); + if (r < 0) + return r; + if (r > 0) /* special case worked! */ + return 0; + if (have_close_range) { + _cleanup_free_ int *sorted_malloc = NULL; + size_t n_sorted; + int *sorted; + /* In the best case we have close_range() to close all fds between a start and an end fd, * which we can use on the "inverted" exception array, i.e. all intervals between all * adjacent pairs from the sorted exception array. This changes loop complexity from O(n) * where n is number of open fds to O(m⋅log(m)) where m is the number of fds to keep * open. Given that we assume n ≫ m that's preferable to us. */ - if (n_except == 0) { - /* Close everything. Yay! */ + assert(n_except < SIZE_MAX); + n_sorted = n_except + 1; - if (close_range(3, -1, 0) >= 0) - return 0; + if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */ + sorted = sorted_malloc = new(int, n_sorted); + else + sorted = newa(int, n_sorted); - if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) - have_close_range = false; - else - return -errno; + if (sorted) { + memcpy(sorted, except, n_except * sizeof(int)); - } else { - typesafe_qsort(except, n_except, cmp_int); + /* Let's add fd 2 to the list of fds, to simplify the loop below, as this + * allows us to cover the head of the array the same way as the body */ + sorted[n_sorted-1] = 2; - for (size_t i = 0; i < n_except; i++) { - int start = i == 0 ? 2 : MAX(except[i-1], 2); /* The first three fds shall always remain open */ - int end = MAX(except[i], 2); + typesafe_qsort(sorted, n_sorted, cmp_int); + + for (size_t i = 0; i < n_sorted-1; i++) { + int start, end; + + start = MAX(sorted[i], 2); /* The first three fds shall always remain open */ + end = MAX(sorted[i+1], 2); assert(end >= start); @@ -250,83 +354,58 @@ int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) { /* Close everything between the start and end fds (both of which shall stay open) */ if (close_range(start + 1, end - 1, 0) < 0) { - if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) - have_close_range = false; - else + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) return -errno; - goto opendir_fallback; + + have_close_range = false; + break; } } - /* The loop succeeded. Let's now close everything beyond the end */ + if (have_close_range) { + /* The loop succeeded. Let's now close everything beyond the end */ - if (except[n_except-1] >= INT_MAX) /* Don't let the addition below overflow */ - return 0; + if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */ + return 0; - int start = MAX(except[n_except-1], 2); + if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0) + return 0; - if (close_range(start + 1, -1, 0) >= 0) - return 0; + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) + return -errno; - if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) have_close_range = false; - else - return -errno; + } } - } - - /* Fallback for when close_range() is not supported */ - opendir_fallback: - d = allow_alloc ? opendir("/proc/self/fd") : NULL; - if (d) { - struct dirent *de; - - FOREACH_DIRENT(de, d, return -errno) { - int fd = -1, q; - - if (safe_atoi(de->d_name, &fd) < 0) - /* Let's better ignore this, just in case */ - continue; - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; + /* Fallback on OOM or if close_range() is not supported */ + } - if (fd_in_set(fd, except, n_except)) - continue; + d = opendir("/proc/self/fd"); + if (!d) + return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */ - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ - r = q; - } + FOREACH_DIRENT(de, d, return -errno) { + int fd = -1, q; - return r; - } - - /* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by - * brute-forcing through the file descriptor table. */ + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; - int max_fd = get_max_fd(); - if (max_fd < 0) - return max_fd; + if (safe_atoi(de->d_name, &fd) < 0) + /* Let's better ignore this, just in case */ + continue; - /* Refuse to do the loop over more too many elements. It's better to fail immediately than to - * spin the CPU for a long time. */ - if (max_fd > MAX_FD_LOOP_LIMIT) - return log_debug_errno(SYNTHETIC_ERRNO(EPERM), - "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.", - max_fd); + if (fd < 3) + continue; - for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { - int q; + if (fd == dirfd(d)) + continue; if (fd_in_set(fd, except, n_except)) continue; q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) + if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ r = q; } @@ -652,7 +731,7 @@ finish: } int fd_reopen(int fd, int flags) { - int new_fd; + int new_fd, r; /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to * turn O_RDWR fds into O_RDONLY fds. @@ -661,15 +740,28 @@ int fd_reopen(int fd, int flags) { * * This implicitly resets the file read index to 0. */ + if (FLAGS_SET(flags, O_DIRECTORY)) { + /* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole + * magic /proc/ directory, and make ourselves independent of that being mounted. */ + new_fd = openat(fd, ".", flags); + if (new_fd < 0) + return -errno; + + return new_fd; + } + new_fd = open(FORMAT_PROC_FD_PATH(fd), flags); if (new_fd < 0) { if (errno != ENOENT) return -errno; - if (proc_mounted() == 0) + r = proc_mounted(); + if (r == 0) return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ - return -ENOENT; + return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is + * not valid, otherwise let's propagate the original + * error */ } return new_fd; @@ -710,9 +802,6 @@ int btrfs_defrag_fd(int fd) { if (r < 0) return r; - if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0) - return -errno; - - return 0; + return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL)); } #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index 2382d52d..f5cfcb4e 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -57,10 +57,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL); int fd_nonblock(int fd, bool nonblock); int fd_cloexec(int fd, bool cloexec); -int close_all_fds_full(int except[], size_t n_except, bool allow_alloc); -static inline int close_all_fds(int except[], size_t n_except) { - return close_all_fds_full(except, n_except, true); -} +int get_max_fd(void); + +int close_all_fds(const int except[], size_t n_except); +int close_all_fds_without_malloc(const int except[], size_t n_except); int same_fd(int a, int b); @@ -91,9 +91,10 @@ static inline int make_null_stdio(void) { /* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ #define TAKE_FD(fd) \ ({ \ - int _fd_ = (fd); \ - (fd) = -1; \ - _fd_; \ + int *_fd_ = &(fd); \ + int _ret_ = *_fd_; \ + *_fd_ = -1; \ + _ret_; \ }) /* Like free_and_replace(), but for file descriptors */ diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index 322638a2..c66bc67b 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -169,7 +169,7 @@ int write_string_stream_ts( * it won't be equal to the new value. */ if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 && streq_skip_trailing_chars(line, t, NEWLINE)) { - log_debug("No change in value '%s', supressing write", line); + log_debug("No change in value '%s', suppressing write", line); return 0; } @@ -551,12 +551,25 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r return !truncated; } -int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) { +int read_virtual_file_at( + int dir_fd, + const char *filename, + size_t max_size, + char **ret_contents, + size_t *ret_size) { + _cleanup_close_ int fd = -1; - assert(filename); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); - fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC); + if (!filename) { + if (dir_fd == AT_FDCWD) + return -EBADF; + + return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size); + } + + fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC); if (fd < 0) return -errno; @@ -934,6 +947,9 @@ DIR *xopendirat(int fd, const char *name, int flags) { assert(!(flags & O_CREAT)); + if (fd == AT_FDCWD && flags == 0) + return opendir(name); + nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); if (nfd < 0) return NULL; diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h index 899def94..cea3dd89 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.h +++ b/src/libnm-systemd-shared/src/basic/fileio.h @@ -69,7 +69,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size } int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size); -int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size); +int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size); +static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) { + return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size); +} static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) { return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size); } diff --git a/src/libnm-systemd-shared/src/basic/format-util.h b/src/libnm-systemd-shared/src/basic/format-util.h index 7dd422b9..7db8b61d 100644 --- a/src/libnm-systemd-shared/src/basic/format-util.h +++ b/src/libnm-systemd-shared/src/basic/format-util.h @@ -26,11 +26,15 @@ assert_cc(sizeof(gid_t) == sizeof(uint32_t)); # error Unknown time_t size #endif -#if defined __x86_64__ && defined __ILP32__ +#if 0 /* NM_IGNORED */ +#if SIZEOF_TIMEX_MEMBER == 8 # define PRI_TIMEX PRIi64 -#else +#elif SIZEOF_TIMEX_MEMBER == 4 # define PRI_TIMEX "li" +#else +# error Unknown timex member size #endif +#endif /* NM_IGNORED */ #if SIZEOF_RLIM_T == 8 # define RLIM_FMT "%" PRIu64 diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index 3f309868..b524a6fd 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -14,6 +14,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hostname-util.h" #include "log.h" #include "macro.h" #include "missing_fcntl.h" @@ -31,18 +32,13 @@ #include "strv.h" #include "time-util.h" #include "tmpfile-util.h" +#include "umask-util.h" #include "user-util.h" #include "util.h" int unlink_noerrno(const char *path) { PROTECT_ERRNO; - int r; - - r = unlink(path); - if (r < 0) - return -errno; - - return 0; + return RET_NERRNO(unlink(path)); } #if 0 /* NM_IGNORED */ @@ -59,7 +55,7 @@ int rmdir_parents(const char *path, const char *stop) { if (!path_is_safe(stop)) return -EINVAL; - p = strdupa(path); + p = strdupa_safe(path); for (;;) { char *slash = NULL; @@ -99,8 +95,8 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char * want — though not atomic (i.e. for a short period both the new and the old filename will exist). */ if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) { - if (unlinkat(olddirfd, oldpath, 0) < 0) { - r = -errno; /* Backup errno before the following unlinkat() alters it */ + r = RET_NERRNO(unlinkat(olddirfd, oldpath, 0)); + if (r < 0) { (void) unlinkat(newdirfd, newpath, 0); return r; } @@ -119,10 +115,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char if (errno != ENOENT) return -errno; - if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0) - return -errno; - - return 0; + return RET_NERRNO(renameat(olddirfd, oldpath, newdirfd, newpath)); } #endif /* NM_IGNORED */ @@ -290,14 +283,9 @@ int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t } int fchmod_umask(int fd, mode_t m) { - mode_t u; - int r; - - u = umask(0777); - r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; - umask(u); + _cleanup_umask_ mode_t u = umask(0777); - return r; + return RET_NERRNO(fchmod(fd, m & (~u))); } int fchmod_opath(int fd, mode_t m) { @@ -550,7 +538,6 @@ int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { int get_files_in_directory(const char *path, char ***list) { _cleanup_strv_free_ char **l = NULL; _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; size_t n = 0; assert(path); @@ -830,7 +817,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; - int fd, r; + int r; r = path_extract_directory(path, &parent); if (r < 0) @@ -844,11 +831,7 @@ int open_parent(const char *path, int flags, mode_t mode) { else if (!FLAGS_SET(flags, O_TMPFILE)) flags |= O_DIRECTORY|O_RDONLY; - fd = open(parent, flags, mode); - if (fd < 0) - return -errno; - - return fd; + return RET_NERRNO(open(parent, flags, mode)); } #endif /* NM_IGNORED */ @@ -962,3 +945,152 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) { return -EINTR; } + +int parse_cifs_service( + const char *s, + char **ret_host, + char **ret_service, + char **ret_path) { + + _cleanup_free_ char *h = NULL, *ss = NULL, *x = NULL; + const char *p, *e, *d; + char delimiter; + + /* Parses a CIFS service in form of //host/service/path… and splitting it in three parts. The last + * part is optional, in which case NULL is returned there. To maximize compatibility syntax with + * backslashes instead of slashes is accepted too. */ + + if (!s) + return -EINVAL; + + p = startswith(s, "//"); + if (!p) { + p = startswith(s, "\\\\"); + if (!p) + return -EINVAL; + } + + delimiter = s[0]; + e = strchr(p, delimiter); + if (!e) + return -EINVAL; + + h = strndup(p, e - p); + if (!h) + return -ENOMEM; + + if (!hostname_is_valid(h, 0)) + return -EINVAL; + + e++; + + d = strchrnul(e, delimiter); + + ss = strndup(e, d - e); + if (!ss) + return -ENOMEM; + + if (!filename_is_valid(ss)) + return -EINVAL; + + if (!isempty(d)) { + x = strdup(skip_leading_chars(d, CHAR_TO_STR(delimiter))); + if (!x) + return -EINVAL; + + /* Make sure to convert Windows-style "\" → Unix-style / */ + for (char *i = x; *i; i++) + if (*i == delimiter) + *i = '/'; + + if (!path_is_valid(x)) + return -EINVAL; + + path_simplify(x); + if (!path_is_normalized(x)) + return -EINVAL; + } + + if (ret_host) + *ret_host = TAKE_PTR(h); + if (ret_service) + *ret_service = TAKE_PTR(ss); + if (ret_path) + *ret_path = TAKE_PTR(x); + + return 0; +} + +int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { + _cleanup_close_ int fd = -1, parent_fd = -1; + _cleanup_free_ char *fname = NULL; + bool made; + int r; + + /* Creates a directory with mkdirat() and then opens it, in the "most atomic" fashion we can + * do. Guarantees that the returned fd refers to a directory. If O_EXCL is specified will fail if the + * dir already exists. Otherwise will open an existing dir, but only if it is one. */ + + if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH)) + return -EINVAL; + if ((flags & O_ACCMODE) != O_RDONLY) + return -EINVAL; + + /* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following + * flags actually make sense to specify: O_CLOEXEC, O_EXCL, O_NOATIME, O_PATH */ + + if (isempty(path)) + return -EINVAL; + + if (!filename_is_valid(path)) { + _cleanup_free_ char *parent = NULL; + + /* If this is not a valid filename, it's a path. Let's open the parent directory then, so + * that we can pin it, and operate below it. */ + + r = path_extract_directory(path, &parent); + if (r < 0) + return r; + + r = path_extract_filename(path, &fname); + if (r < 0) + return r; + + parent_fd = openat(dirfd, parent, O_PATH|O_DIRECTORY|O_CLOEXEC); + if (parent_fd < 0) + return -errno; + + dirfd = parent_fd; + path = fname; + } + + r = RET_NERRNO(mkdirat(dirfd, path, mode)); + if (r == -EEXIST) { + if (FLAGS_SET(flags, O_EXCL)) + return -EEXIST; + + made = false; + } else if (r < 0) + return r; + else + made = true; + + fd = RET_NERRNO(openat(dirfd, path, (flags & ~O_EXCL)|O_DIRECTORY|O_NOFOLLOW)); + if (fd < 0) { + if (fd == -ENOENT) /* We got ENOENT? then someone else immediately removed it after we + * created it. In that case let's return immediately without unlinking + * anything, because there simply isn't anything to unlink anymore. */ + return -ENOENT; + if (fd == -ELOOP) /* is a symlink? exists already → created by someone else, don't unlink */ + return -EEXIST; + if (fd == -ENOTDIR) /* not a directory? exists already → created by someone else, don't unlink */ + return -EEXIST; + + if (made) + (void) unlinkat(dirfd, path, AT_REMOVEDIR); + + return fd; + } + + return TAKE_FD(fd); +} diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index f8a7657a..0bbb3f62 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -47,7 +47,7 @@ int fd_warn_permissions(const char *path, int fd); int stat_warn_permissions(const char *path, const struct stat *st); #define laccess(path, mode) \ - (faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) < 0 ? -errno : 0) + RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)) int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); int touch(const char *path); @@ -106,3 +106,7 @@ static inline int conservative_rename(const char *oldpath, const char *newpath) } int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); + +int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path); + +int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode); diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c index 3063cc7c..1d1c2f51 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.c +++ b/src/libnm-systemd-shared/src/basic/hostname-util.c @@ -49,8 +49,7 @@ int gethostname_full(GetHostnameFlags flags, char **ret) { assert_se(uname(&u) >= 0); s = u.nodename; - if (isempty(s) || - (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) || + if (isempty(s) || streq(s, "(none)") || (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) || (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) { if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT)) diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h index 0d1574db..d435bed5 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.h +++ b/src/libnm-systemd-shared/src/basic/hostname-util.h @@ -9,10 +9,9 @@ #include "strv.h" typedef enum GetHostnameFlags { - GET_HOSTNAME_ALLOW_NONE = 1 << 0, /* accepts "(none)". */ - GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 1, /* accepts "localhost" or friends. */ - GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */ - GET_HOSTNAME_SHORT = 1 << 3, /* kills the FQDN part if present. */ + GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 0, /* accepts "localhost" or friends. */ + GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */ + GET_HOSTNAME_SHORT = 1 << 2, /* kills the FQDN part if present. */ } GetHostnameFlags; int gethostname_full(GetHostnameFlags flags, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c index bd4dd8fc..1bf0764d 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -629,65 +629,120 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas return 0; } -#if 0 /* NM_IGNORED */ -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { +int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen) { + struct in_addr mask; + assert(addr); - if (family == AF_INET) { - struct in_addr mask; + if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen)) + return -EINVAL; - if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen)) - return -EINVAL; + addr->s_addr &= mask.s_addr; + return 0; +} - addr->in.s_addr &= mask.s_addr; - return 0; - } +int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen) { + unsigned i; - if (family == AF_INET6) { - unsigned i; + for (i = 0; i < 16; i++) { + uint8_t mask; - for (i = 0; i < 16; i++) { - uint8_t mask; + if (prefixlen >= 8) { + mask = 0xFF; + prefixlen -= 8; + } else if (prefixlen > 0) { + mask = 0xFF << (8 - prefixlen); + prefixlen = 0; + } else { + assert(prefixlen == 0); + mask = 0; + } - if (prefixlen >= 8) { - mask = 0xFF; - prefixlen -= 8; - } else { - mask = 0xFF << (8 - prefixlen); - prefixlen = 0; - } + addr->s6_addr[i] &= mask; + } - addr->in6.s6_addr[i] &= mask; - } + return 0; +} - return 0; +#if 0 /* NM_IGNORED */ +int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { + assert(addr); + + switch (family) { + case AF_INET: + return in4_addr_mask(&addr->in, prefixlen); + case AF_INET6: + return in6_addr_mask(&addr->in6, prefixlen); + default: + return -EAFNOSUPPORT; } +} - return -EAFNOSUPPORT; +int in4_addr_prefix_covers( + const struct in_addr *prefix, + unsigned char prefixlen, + const struct in_addr *address) { + + struct in_addr masked_prefix, masked_address; + int r; + + assert(prefix); + assert(address); + + masked_prefix = *prefix; + r = in4_addr_mask(&masked_prefix, prefixlen); + if (r < 0) + return r; + + masked_address = *address; + r = in4_addr_mask(&masked_address, prefixlen); + if (r < 0) + return r; + + return in4_addr_equal(&masked_prefix, &masked_address); } -int in_addr_prefix_covers(int family, - const union in_addr_union *prefix, - unsigned char prefixlen, - const union in_addr_union *address) { +int in6_addr_prefix_covers( + const struct in6_addr *prefix, + unsigned char prefixlen, + const struct in6_addr *address) { - union in_addr_union masked_prefix, masked_address; + struct in6_addr masked_prefix, masked_address; int r; assert(prefix); assert(address); masked_prefix = *prefix; - r = in_addr_mask(family, &masked_prefix, prefixlen); + r = in6_addr_mask(&masked_prefix, prefixlen); if (r < 0) return r; masked_address = *address; - r = in_addr_mask(family, &masked_address, prefixlen); + r = in6_addr_mask(&masked_address, prefixlen); if (r < 0) return r; - return in_addr_equal(family, &masked_prefix, &masked_address); + return in6_addr_equal(&masked_prefix, &masked_address); +} + +int in_addr_prefix_covers( + int family, + const union in_addr_union *prefix, + unsigned char prefixlen, + const union in_addr_union *address) { + + assert(prefix); + assert(address); + + switch (family) { + case AF_INET: + return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in); + case AF_INET6: + return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6); + default: + return -EAFNOSUPPORT; + } } int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { @@ -855,4 +910,10 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) { } DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( + in6_addr_hash_ops_free, + struct in6_addr, + in6_addr_hash_func, + in6_addr_compare_func, + free); #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h index c74b0d51..0178391e 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -89,7 +89,11 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); +int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen); +int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen); int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); +int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address); +int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address); int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); @@ -110,6 +114,13 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) { return family == AF_INET6 ? 16 : 4; } +#define FAMILY_ADDRESS_SIZE_SAFE(f) \ + ({ \ + int _f = (f); \ + _f == AF_INET ? sizeof(struct in_addr) : \ + _f == AF_INET6 ? sizeof(struct in6_addr) : 0; \ + }) + /* Workaround for clang, explicitly specify the maximum-size element here. * See also oss-fuzz#11344. */ #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} }) @@ -119,6 +130,7 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b); extern const struct hash_ops in_addr_data_hash_ops; extern const struct hash_ops in6_addr_hash_ops; +extern const struct hash_ops in6_addr_hash_ops_free; #define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u" #define IPV4_ADDRESS_FMT_VAL(address) \ diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.c b/src/libnm-systemd-shared/src/basic/inotify-util.c index cc2a92ad..59e03e62 100644 --- a/src/libnm-systemd-shared/src/basic/inotify-util.c +++ b/src/libnm-systemd-shared/src/basic/inotify-util.c @@ -4,14 +4,26 @@ #include "fd-util.h" #include "inotify-util.h" +#include "stat-util.h" int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - int wd; + int wd, r; /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask); - if (wd < 0) - return -errno; + if (wd < 0) { + if (errno != ENOENT) + return -errno; + + /* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */ + r = proc_mounted(); + if (r == 0) + return -ENOSYS; + if (r > 0) + return -EBADF; + + return -ENOENT; /* OK, no clue, let's propagate the original error */ + } return wd; } diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h index e488fff9..f827e721 100644 --- a/src/libnm-systemd-shared/src/basic/list.h +++ b/src/libnm-systemd-shared/src/basic/list.h @@ -134,7 +134,7 @@ } while (false) #define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ + (!(item)->name##_prev && !(item)->name##_next) #define LIST_FOREACH(name,i,head) \ for ((i) = (head); (i); (i) = (i)->name##_next) @@ -181,3 +181,12 @@ } \ (b) = NULL; \ } while (false) + +#define LIST_POP(name, a) \ + ({ \ + typeof(a)* _a = &(a); \ + typeof(a) _p = *_a; \ + if (_p) \ + LIST_REMOVE(name, *_a, _p); \ + _p; \ + }) diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index b11eac21..88d278fd 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -27,10 +27,14 @@ typedef enum LogTarget{ _LOG_TARGET_INVALID = -EINVAL, } LogTarget; -/* Note to readers: << and >> have lower precedence than & and | */ +/* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be + * used a regular log level. */ +#define LOG_NULL (LOG_EMERG - 1) + +/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */ #define SYNTHETIC_ERRNO(num) (1 << 30 | (num)) #define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) -#define ERRNO_VALUE(val) (abs(val) & 255) +#define ERRNO_VALUE(val) (abs(val) & ~(1 << 30)) /* The callback function to be invoked when syntax warnings are seen * in the unit files. */ @@ -90,6 +94,7 @@ int log_open(void); void log_close(void); void log_forget_fds(void); +void log_parse_environment_variables(void); void log_parse_environment(void); #if 0 /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index 33d28332..79437775 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -25,7 +25,7 @@ #define _public_ __attribute__((__visibility__("default"))) #define _hidden_ __attribute__((__visibility__("hidden"))) #define _weakref_(x) __attribute__((__weakref__(#x))) -#define _alignas_(x) __attribute__((__aligned__(__alignof(x)))) +#define _alignas_(x) __attribute__((__aligned__(__alignof__(x)))) #define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) #define _warn_unused_result_ __attribute__((__warn_unused_result__)) @@ -154,24 +154,6 @@ #define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) #define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) -static inline size_t ALIGN_TO(size_t l, size_t ali) { - /* Check that alignment is exponent of 2 */ -#if SIZE_MAX == UINT_MAX - assert(__builtin_popcount(ali) == 1); -#elif SIZE_MAX == ULONG_MAX - assert(__builtin_popcountl(ali) == 1); -#elif SIZE_MAX == ULLONG_MAX - assert(__builtin_popcountll(ali) == 1); -#else -#error "Unexpected size_t" -#endif - - if (l > SIZE_MAX - (ali - 1)) - return SIZE_MAX; /* indicate overflow */ - - return ((l + ali - 1) & ~(ali - 1)); -} - #define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ @@ -355,13 +337,6 @@ static inline int __coverity_check_and_return__(int condition) { ans; \ }) -#define UPDATE_FLAG(orig, flag, b) \ - ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) -#define SET_FLAG(v, flag, b) \ - (v) = UPDATE_FLAG(v, flag, b) -#define FLAGS_SET(v, flags) \ - ((~(v) & (flags)) == 0) - #define SWAP_TWO(x, y) do { \ typeof(x) _t = (x); \ (x) = (y); \ diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index aeb994f0..41c83ec5 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -20,6 +20,7 @@ #include <asm/sgidefs.h> #endif +#include "macro.h" #include "missing_keyctl.h" #include "missing_stat.h" #include "missing_syscall_def.h" @@ -79,7 +80,8 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) { /* ======================================================================= */ #if !HAVE_GETRANDOM -static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) { +/* glibc says getrandom() returns ssize_t */ +static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) { # ifdef __NR_getrandom return syscall(__NR_getrandom, buffer, count, flags); # else @@ -466,8 +468,52 @@ struct mount_attr { struct mount_attr; #endif +#ifndef MOUNT_ATTR_RDONLY +#define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */ +#endif + +#ifndef MOUNT_ATTR_NOSUID +#define MOUNT_ATTR_NOSUID 0x00000002 /* Ignore suid and sgid bits */ +#endif + +#ifndef MOUNT_ATTR_NODEV +#define MOUNT_ATTR_NODEV 0x00000004 /* Disallow access to device special files */ +#endif + +#ifndef MOUNT_ATTR_NOEXEC +#define MOUNT_ATTR_NOEXEC 0x00000008 /* Disallow program execution */ +#endif + +#ifndef MOUNT_ATTR__ATIME +#define MOUNT_ATTR__ATIME 0x00000070 /* Setting on how atime should be updated */ +#endif + +#ifndef MOUNT_ATTR_RELATIME +#define MOUNT_ATTR_RELATIME 0x00000000 /* - Update atime relative to mtime/ctime. */ +#endif + +#ifndef MOUNT_ATTR_NOATIME +#define MOUNT_ATTR_NOATIME 0x00000010 /* - Do not update access times. */ +#endif + +#ifndef MOUNT_ATTR_STRICTATIME +#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */ +#endif + +#ifndef MOUNT_ATTR_NODIRATIME +#define MOUNT_ATTR_NODIRATIME 0x00000080 /* Do not update directory access times */ +#endif + #ifndef MOUNT_ATTR_IDMAP -#define MOUNT_ATTR_IDMAP 0x00100000 +#define MOUNT_ATTR_IDMAP 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */ +#endif + +#ifndef MOUNT_ATTR_NOSYMFOLLOW +#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks */ +#endif + +#ifndef MOUNT_ATTR_SIZE_VER0 +#define MOUNT_ATTR_SIZE_VER0 32 /* sizeof first published struct */ #endif #ifndef AT_RECURSIVE @@ -545,4 +591,20 @@ static inline int missing_move_mount( # define move_mount missing_move_mount #endif + +/* ======================================================================= */ + +#if !HAVE_GETDENTS64 + +static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) { +# if defined __NR_getdents64 && __NR_getdents64 >= 0 + return syscall(__NR_getdents64, fd, buffer, length); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define getdents64 missing_getdents64 +#endif #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index f81bf1da..4ba2657c 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -651,7 +651,7 @@ int parse_ip_port(const char *s, uint16_t *ret) { uint16_t l; int r; - r = safe_atou16(s, &l); + r = safe_atou16_full(s, SAFE_ATO_REFUSE_LEADING_WHITESPACE, &l); if (r < 0) return r; @@ -711,7 +711,7 @@ int parse_dev(const char *s, dev_t *ret) { if (s[n] != ':') return -EINVAL; - major = strndupa(s, n); + major = strndupa_safe(s, n); r = safe_atou(major, &x); if (r < 0) return r; @@ -772,7 +772,7 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) { if (!d) return -EINVAL; - i_str = strndupa(s, d - s); + i_str = strndupa_safe(s, d - s); f_str = d + 1; r = safe_atolu_full(i_str, 10, &i); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index 92c55048..f391848f 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -1246,8 +1246,6 @@ char *file_in_same_dir(const char *path, const char *filename) { } bool hidden_or_backup_file(const char *filename) { - const char *p; - assert(filename); if (filename[0] == '.' || @@ -1257,24 +1255,25 @@ bool hidden_or_backup_file(const char *filename) { endswith(filename, "~")) return true; - p = strrchr(filename, '.'); - if (!p) + const char *dot = strrchr(filename, '.'); + if (!dot) return false; - /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up - * with always new suffixes and that everybody else should just adjust to that, then it really should be on - * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt - * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional - * string. Specifically: there's now: + /* Please, let's not add more entries to the list below. If external projects think it's a good idea + * to come up with always new suffixes and that everybody else should just adjust to that, then it + * really should be on them. Hence, in future, let's not add any more entries. Instead, let's ask + * those packages to instead adopt one of the generic suffixes/prefixes for hidden files or backups, + * possibly augmented with an additional string. Specifically: there's now: * * The generic suffixes "~" and ".bak" for backup files * The generic prefix "." for hidden files * - * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" - * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. + * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", + * ".foopkg-dist" or so registered, let's refuse that and ask them to use ".foopkg.new", + * ".foopkg.old" or ".foopkg~" instead. */ - return STR_IN_SET(p + 1, + return STR_IN_SET(dot + 1, "rpmnew", "rpmsave", "rpmorig", @@ -1296,15 +1295,16 @@ bool hidden_or_backup_file(const char *filename) { bool is_device_path(const char *path) { - /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ + /* Returns true for paths that likely refer to a device, either by path in sysfs or to something in + * /dev. */ return PATH_STARTSWITH_SET(path, "/dev/", "/sys/"); } bool valid_device_node_path(const char *path) { - /* Some superficial checks whether the specified path is a valid device node path, all without looking at the - * actual device node. */ + /* Some superficial checks whether the specified path is a valid device node path, all without + * looking at the actual device node. */ if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")) return false; @@ -1318,8 +1318,8 @@ bool valid_device_node_path(const char *path) { bool valid_device_allow_pattern(const char *path) { assert(path); - /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= - * accept it */ + /* Like valid_device_node_path(), but also allows full-subsystem expressions like those accepted by + * DeviceAllow= and DeviceDeny=. */ if (STARTSWITH_SET(path, "block-", "char-")) return true; @@ -1412,8 +1412,8 @@ bool dot_or_dot_dot(const char *path) { #if 0 /* NM_IGNORED */ bool empty_or_root(const char *path) { - /* For operations relative to some root directory, returns true if the specified root directory is redundant, - * i.e. either / or NULL or the empty string or any equivalent. */ + /* For operations relative to some root directory, returns true if the specified root directory is + * redundant, i.e. either / or NULL or the empty string or any equivalent. */ if (isempty(path)) return true; diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 96d0912c..70564bd3 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -187,13 +187,13 @@ static int get_process_cmdline_nulstr( return r; } -int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) { +int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) { _cleanup_free_ char *t = NULL; size_t k; char *ans; - assert(line); assert(pid >= 0); + assert(ret); /* Retrieve and format a commandline. See above for discussion of retrieval options. * @@ -220,25 +220,23 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags assert(!(flags & PROCESS_CMDLINE_USE_LOCALE)); _cleanup_strv_free_ char **args = NULL; + char **p; args = strv_parse_nulstr(t, k); if (!args) return -ENOMEM; - for (size_t i = 0; args[i]; i++) { - char *e; - - e = shell_maybe_quote(args[i], shflags); - if (!e) - return -ENOMEM; + /* Drop trailing empty strings. See issue #21186. */ + STRV_FOREACH_BACKWARDS(p, args) { + if (!isempty(*p)) + break; - free_and_replace(args[i], e); + *p = mfree(*p); } - ans = strv_join(args, " "); + ans = quote_command_line(args, shflags); if (!ans) return -ENOMEM; - } else { /* Arguments are separated by NULs. Let's replace those with spaces. */ for (size_t i = 0; i < k - 1; i++) @@ -257,7 +255,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags ans = str_realloc(ans); } - *line = ans; + *ret = ans; return 0; } @@ -464,29 +462,29 @@ int is_kernel_thread(pid_t pid) { return !!(flags & PF_KTHREAD); } -int get_process_capeff(pid_t pid, char **capeff) { +int get_process_capeff(pid_t pid, char **ret) { const char *p; int r; - assert(capeff); assert(pid >= 0); + assert(ret); p = procfs_file_alloca(pid, "status"); - r = get_proc_field(p, "CapEff", WHITESPACE, capeff); + r = get_proc_field(p, "CapEff", WHITESPACE, ret); if (r == -ENOENT) return -ESRCH; return r; } -static int get_process_link_contents(const char *proc_file, char **name) { +static int get_process_link_contents(const char *proc_file, char **ret) { int r; assert(proc_file); - assert(name); + assert(ret); - r = readlink_malloc(proc_file, name); + r = readlink_malloc(proc_file, ret); if (r == -ENOENT) return -ESRCH; if (r < 0) @@ -495,32 +493,33 @@ static int get_process_link_contents(const char *proc_file, char **name) { return 0; } -int get_process_exe(pid_t pid, char **name) { +int get_process_exe(pid_t pid, char **ret) { const char *p; char *d; int r; assert(pid >= 0); + assert(ret); p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, name); + r = get_process_link_contents(p, ret); if (r < 0) return r; - d = endswith(*name, " (deleted)"); + d = endswith(*ret, " (deleted)"); if (d) *d = '\0'; return 0; } -static int get_process_id(pid_t pid, const char *field, uid_t *uid) { +static int get_process_id(pid_t pid, const char *field, uid_t *ret) { _cleanup_fclose_ FILE *f = NULL; const char *p; int r; assert(field); - assert(uid); + assert(ret); if (pid < 0) return -EINVAL; @@ -550,60 +549,62 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { l[strcspn(l, WHITESPACE)] = 0; - return parse_uid(l, uid); + return parse_uid(l, ret); } } return -EIO; } -int get_process_uid(pid_t pid, uid_t *uid) { +int get_process_uid(pid_t pid, uid_t *ret) { if (pid == 0 || pid == getpid_cached()) { - *uid = getuid(); + *ret = getuid(); return 0; } - return get_process_id(pid, "Uid:", uid); + return get_process_id(pid, "Uid:", ret); } -int get_process_gid(pid_t pid, gid_t *gid) { +int get_process_gid(pid_t pid, gid_t *ret) { if (pid == 0 || pid == getpid_cached()) { - *gid = getgid(); + *ret = getgid(); return 0; } assert_cc(sizeof(uid_t) == sizeof(gid_t)); - return get_process_id(pid, "Gid:", gid); + return get_process_id(pid, "Gid:", ret); } -int get_process_cwd(pid_t pid, char **cwd) { +int get_process_cwd(pid_t pid, char **ret) { const char *p; assert(pid >= 0); + assert(ret); if (pid == 0 || pid == getpid_cached()) - return safe_getcwd(cwd); + return safe_getcwd(ret); p = procfs_file_alloca(pid, "cwd"); - return get_process_link_contents(p, cwd); + return get_process_link_contents(p, ret); } -int get_process_root(pid_t pid, char **root) { +int get_process_root(pid_t pid, char **ret) { const char *p; assert(pid >= 0); + assert(ret); p = procfs_file_alloca(pid, "root"); - return get_process_link_contents(p, root); + return get_process_link_contents(p, ret); } #define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U) -int get_process_environ(pid_t pid, char **env) { +int get_process_environ(pid_t pid, char **ret) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *outcome = NULL; size_t sz = 0; @@ -611,7 +612,7 @@ int get_process_environ(pid_t pid, char **env) { int r; assert(pid >= 0); - assert(env); + assert(ret); p = procfs_file_alloca(pid, "environ"); @@ -643,7 +644,7 @@ int get_process_environ(pid_t pid, char **env) { } outcome[sz] = '\0'; - *env = TAKE_PTR(outcome); + *ret = TAKE_PTR(outcome); return 0; } @@ -702,13 +703,13 @@ int get_process_ppid(pid_t pid, pid_t *ret) { return 0; } -int get_process_umask(pid_t pid, mode_t *umask) { +int get_process_umask(pid_t pid, mode_t *ret) { _cleanup_free_ char *m = NULL; const char *p; int r; - assert(umask); assert(pid >= 0); + assert(ret); p = procfs_file_alloca(pid, "status"); @@ -716,7 +717,7 @@ int get_process_umask(pid_t pid, mode_t *umask) { if (r == -ENOENT) return -ESRCH; - return parse_mode(m, umask); + return parse_mode(m, ret); } int wait_for_terminate(pid_t pid, siginfo_t *status) { @@ -830,7 +831,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { if (n >= until) break; - r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0; + r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n))); /* Assuming we woke due to the child exiting. */ if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { if (status.si_pid == pid) { @@ -863,8 +864,8 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { void sigkill_wait(pid_t pid) { assert(pid > 1); - if (kill(pid, SIGKILL) >= 0) - (void) wait_for_terminate(pid, NULL); + (void) kill(pid, SIGKILL); + (void) wait_for_terminate(pid, NULL); } void sigkill_waitp(pid_t *pid) { @@ -881,14 +882,14 @@ void sigkill_waitp(pid_t *pid) { void sigterm_wait(pid_t pid) { assert(pid > 1); - if (kill_and_sigcont(pid, SIGTERM) >= 0) - (void) wait_for_terminate(pid, NULL); + (void) kill_and_sigcont(pid, SIGTERM); + (void) wait_for_terminate(pid, NULL); } int kill_and_sigcont(pid_t pid, int sig) { int r; - r = kill(pid, sig) < 0 ? -errno : 0; + r = RET_NERRNO(kill(pid, sig)); /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't * affected by a process being suspended anyway. */ @@ -1064,8 +1065,8 @@ unsigned long personality_from_string(const char *p) { if (architecture == native_architecture()) return PER_LINUX; -#ifdef SECONDARY_ARCHITECTURE - if (architecture == SECONDARY_ARCHITECTURE) +#ifdef ARCHITECTURE_SECONDARY + if (architecture == ARCHITECTURE_SECONDARY) return PER_LINUX32; #endif @@ -1077,9 +1078,9 @@ const char* personality_to_string(unsigned long p) { if (p == PER_LINUX) architecture = native_architecture(); -#ifdef SECONDARY_ARCHITECTURE +#ifdef ARCHITECTURE_SECONDARY else if (p == PER_LINUX32) - architecture = SECONDARY_ARCHITECTURE; + architecture = ARCHITECTURE_SECONDARY; #endif if (architecture < 0) @@ -1150,23 +1151,6 @@ int pid_compare_func(const pid_t *a, const pid_t *b) { /* Suitable for usage in qsort() */ return CMP(*a, *b); } - -int ioprio_parse_priority(const char *s, int *ret) { - int i, r; - - assert(s); - assert(ret); - - r = safe_atoi(s, &i); - if (r < 0) - return r; - - if (!ioprio_priority_is_valid(i)) - return -EINVAL; - - *ret = i; - return 0; -} #endif /* NM_IGNORED */ /* The cached PID, possible values: @@ -1256,7 +1240,7 @@ static void restore_sigsetp(sigset_t **ssp) { int safe_fork_full( const char *name, - int except_fds[], + const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid) { @@ -1451,7 +1435,7 @@ int safe_fork_full( int namespace_fork( const char *outer_name, const char *inner_name, - int except_fds[], + const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, @@ -1467,8 +1451,7 @@ int namespace_fork( * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that * /proc/self/fd works correctly. */ - r = safe_fork_full(outer_name, except_fds, n_except_fds, - (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid); + r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid); if (r < 0) return r; if (r == 0) { @@ -1513,7 +1496,7 @@ int set_oom_score_adjust(int value) { } int get_oom_score_adjust(int *ret) { - _cleanup_free_ char *t; + _cleanup_free_ char *t = NULL; int r, a; r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL); @@ -1628,14 +1611,27 @@ bool invoked_as(char *argv[], const char *token) { return strstr(last_path_component(argv[0]), token); } -static const char *const ioprio_class_table[] = { - [IOPRIO_CLASS_NONE] = "none", - [IOPRIO_CLASS_RT] = "realtime", - [IOPRIO_CLASS_BE] = "best-effort", - [IOPRIO_CLASS_IDLE] = "idle", -}; +_noreturn_ void freeze(void) { + log_close(); + + /* Make sure nobody waits for us (i.e. on one of our sockets) anymore. Note that we use + * close_all_fds_without_malloc() instead of plain close_all_fds() here, since we want this function + * to be compatible with being called from signal handlers. */ + (void) close_all_fds_without_malloc(NULL, 0); + + /* Let's not freeze right away, but keep reaping zombies. */ + for (;;) { + siginfo_t si = {}; + + if (waitid(P_ALL, 0, &si, WEXITED) < 0 && errno != EINTR) + break; + } + + /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ + for (;;) + pause(); +} -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES); static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index bc16d328..a957cab2 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -14,7 +14,6 @@ #include "alloc-util.h" #include "format-util.h" #include "macro.h" -#include "missing_ioprio.h" #include "time-util.h" #define procfs_file_alloca(pid, field) \ @@ -39,17 +38,17 @@ typedef enum ProcessCmdlineFlags { PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3, } ProcessCmdlineFlags; -int get_process_comm(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line); -int get_process_exe(pid_t pid, char **name); -int get_process_uid(pid_t pid, uid_t *uid); -int get_process_gid(pid_t pid, gid_t *gid); -int get_process_capeff(pid_t pid, char **capeff); -int get_process_cwd(pid_t pid, char **cwd); -int get_process_root(pid_t pid, char **root); -int get_process_environ(pid_t pid, char **environ); -int get_process_ppid(pid_t pid, pid_t *ppid); -int get_process_umask(pid_t pid, mode_t *umask); +int get_process_comm(pid_t pid, char **ret); +int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret); +int get_process_exe(pid_t pid, char **ret); +int get_process_uid(pid_t pid, uid_t *ret); +int get_process_gid(pid_t pid, gid_t *ret); +int get_process_capeff(pid_t pid, char **ret); +int get_process_cwd(pid_t pid, char **ret); +int get_process_root(pid_t pid, char **ret); +int get_process_environ(pid_t pid, char **ret); +int get_process_ppid(pid_t pid, pid_t *ret); +int get_process_umask(pid_t pid, mode_t *ret); int wait_for_terminate(pid_t pid, siginfo_t *status); @@ -97,9 +96,6 @@ const char *personality_to_string(unsigned long); int safe_personality(unsigned long p); int opinionated_personality(unsigned long *ret); -int ioprio_class_to_string_alloc(int i, char **s); -int ioprio_class_from_string(const char *s); - const char *sigchld_code_to_string(int i) _const_; int sigchld_code_from_string(const char *s) _pure_; @@ -131,21 +127,11 @@ static inline bool sched_priority_is_valid(int i) { return i >= 0 && i <= sched_get_priority_max(SCHED_RR); } -static inline bool ioprio_class_is_valid(int i) { - return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); -} - -static inline bool ioprio_priority_is_valid(int i) { - return i >= 0 && i < IOPRIO_BE_NR; -} - static inline bool pid_is_valid(pid_t p) { return p > 0; } #endif /* NM_IGNORED */ -int ioprio_parse_priority(const char *s, int *ret); - pid_t getpid_cached(void); void reset_cached_pid(void); @@ -168,13 +154,13 @@ typedef enum ForkFlags { FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */ } ForkFlags; -int safe_fork_full(const char *name, int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); +int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { return safe_fork_full(name, NULL, 0, flags, ret_pid); } -int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid); +int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid); int set_oom_score_adjust(int value); int get_oom_score_adjust(int *ret); @@ -194,8 +180,9 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX); /* Like TAKE_PTR() but for child PIDs, resetting them to 0 */ #define TAKE_PID(pid) \ ({ \ - pid_t _pid_ = (pid); \ - (pid) = 0; \ + pid_t *_ppid_ = &(pid); \ + pid_t _pid_ = *_ppid_; \ + *_ppid_ = 0; \ _pid_; \ }) @@ -204,3 +191,5 @@ int pidfd_get_pid(int fd, pid_t *ret); int setpriority_closest(int priority); bool invoked_as(char *argv[], const char *token); + +_noreturn_ void freeze(void); diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index 063784dc..9832f8ef 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -163,7 +163,6 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { static int have_syscall = -1; _cleanup_close_ int fd = -1; bool got_some = false; - int r; /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK @@ -222,25 +221,26 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { for (;;) { + ssize_t l; #if !HAVE_GETRANDOM /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. * If you don't compile against a libc that provides getrandom(), you don't get it. */ - r = -1; + l = -1; errno = ENOSYS; #else - r = getrandom(p, n, + l = getrandom(p, n, (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); #endif - if (r > 0) { + if (l > 0) { have_syscall = true; - if ((size_t) r == n) + if ((size_t) l == n) return 0; /* Yay, success! */ - assert((size_t) r < n); - p = (uint8_t*) p + r; - n -= r; + assert((size_t) l < n); + p = (uint8_t*) p + l; + n -= l; if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { /* Fill in the remaining bytes using pseudo-random values */ @@ -257,7 +257,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { /* Fill in the rest with /dev/urandom */ break; - } else if (r == 0) { + } else if (l == 0) { have_syscall = true; return -EIO; diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c index f96eb4d4..6d8c9920 100644 --- a/src/libnm-systemd-shared/src/basic/signal-util.c +++ b/src/libnm-systemd-shared/src/basic/signal-util.c @@ -5,6 +5,7 @@ #include <errno.h> #include <stdarg.h> +#include "errno-util.h" #include "macro.h" #include "parse-util.h" #include "signal-util.h" @@ -42,10 +43,7 @@ int reset_signal_mask(void) { if (sigemptyset(&ss) < 0) return -errno; - if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) - return -errno; - - return 0; + return RET_NERRNO(sigprocmask(SIG_SETMASK, &ss, NULL)); } int sigaction_many_internal(const struct sigaction *sa, ...) { @@ -122,7 +120,7 @@ int sigprocmask_many(int how, sigset_t *old, ...) { return 0; } -static const char *const __signal_table[] = { +static const char *const static_signal_table[] = { [SIGHUP] = "HUP", [SIGINT] = "INT", [SIGQUIT] = "QUIT", @@ -158,13 +156,13 @@ static const char *const __signal_table[] = { [SIGSYS] = "SYS" }; -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int); const char *signal_to_string(int signo) { static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)]; const char *name; - name = __signal_to_string(signo); + name = static_signal_to_string(signo); if (name) return name; @@ -193,7 +191,7 @@ int signal_from_string(const char *s) { s += 3; /* Check that the input is a signal name. */ - signo = __signal_from_string(s); + signo = static_signal_from_string(s); if (signo > 0) return signo; @@ -250,11 +248,7 @@ int signal_is_blocked(int sig) { if (r != 0) return -r; - r = sigismember(&ss, sig); - if (r < 0) - return -errno; - - return r; + return RET_NERRNO(sigismember(&ss, sig)); } int pop_pending_signal_internal(int sig, ...) { diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c index 49a4df44..6478fdee 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.c +++ b/src/libnm-systemd-shared/src/basic/socket-util.c @@ -405,6 +405,41 @@ const union in_addr_union *sockaddr_in_addr(const struct sockaddr *_sa) { } } +int sockaddr_set_in_addr( + union sockaddr_union *u, + int family, + const union in_addr_union *a, + uint16_t port) { + + assert(u); + assert(a); + + switch (family) { + + case AF_INET: + u->in = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_addr = a->in, + .sin_port = htobe16(port), + }; + + return 0; + + case AF_INET6: + u->in6 = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + .sin6_addr = a->in6, + .sin6_port = htobe16(port), + }; + + return 0; + + default: + return -EAFNOSUPPORT; + + } +} + int sockaddr_pretty( const struct sockaddr *_sa, socklen_t salen, @@ -556,7 +591,7 @@ int getpeername_pretty(int fd, bool include_port, char **ret) { return -errno; if (sa.sa.sa_family == AF_UNIX) { - struct ucred ucred = {}; + struct ucred ucred = UCRED_INVALID; /* UNIX connection sockets are anonymous, so let's use * PID/UID as pretty credentials instead */ @@ -1238,10 +1273,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) { /* Call with NULL to drop binding */ - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0) - return -errno; - - return 0; + return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname))); } int socket_bind_to_ifindex(int fd, int ifindex) { @@ -1250,13 +1282,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) { assert(fd >= 0); - if (ifindex <= 0) { + if (ifindex <= 0) /* Drop binding */ - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0) - return -errno; - - return 0; - } + return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0)); r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex); if (r != -ENOPROTOOPT) @@ -1345,16 +1373,10 @@ int socket_set_unicast_if(int fd, int af, int ifi) { switch (af) { case AF_INET: - if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) - return -errno; - - return 0; + return RET_NERRNO(setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be))); case AF_INET6: - if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) - return -errno; - - return 0; + return RET_NERRNO(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be))); default: return -EAFNOSUPPORT; diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h index 4caa27c8..ddea5350 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.h +++ b/src/libnm-systemd-shared/src/basic/socket-util.h @@ -15,6 +15,7 @@ #include <sys/un.h> #include "errno-util.h" +#include "in-addr-util.h" #include "macro.h" #include "missing_network.h" #include "missing_socket.h" @@ -108,6 +109,7 @@ bool socket_ipv6_is_enabled(void); int sockaddr_port(const struct sockaddr *_sa, unsigned *port); const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa); +int sockaddr_set_in_addr(union sockaddr_union *u, int family, const union in_addr_union *a, uint16_t port); int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); int getpeername_pretty(int fd, bool include_port, char **ret); @@ -331,3 +333,6 @@ static inline int socket_set_recvfragsize(int fd, int af, bool b) { #endif /* NM_IGNORED */ int socket_get_mtu(int fd, int af, size_t *ret); + +/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */ +#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID } diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h index a8984fc1..28c06c40 100644 --- a/src/libnm-systemd-shared/src/basic/sort-util.h +++ b/src/libnm-systemd-shared/src/basic/sort-util.h @@ -5,14 +5,20 @@ #include "macro.h" +/* This is the same as glibc's internal __compar_d_fn_t type. glibc exports a public comparison_fn_t, for the + * external type __compar_fn_t, but doesn't do anything similar for __compar_d_fn_t. Let's hence do that + * ourselves, picking a name that is obvious, but likely enough to not clash with glibc's choice of naming if + * they should ever add one. */ +typedef int (*comparison_userdata_fn_t)(const void *, const void *, void *); + void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - __compar_d_fn_t compar, void *arg); + comparison_userdata_fn_t compar, void *arg); #define typesafe_bsearch_r(k, b, n, func, userdata) \ ({ \ const typeof(b[0]) *_k = k; \ int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ - xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \ + xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) /** @@ -20,7 +26,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, * that only if nmemb > 0. */ static inline void* bsearch_safe(const void *key, const void *base, - size_t nmemb, size_t size, __compar_fn_t compar) { + size_t nmemb, size_t size, comparison_fn_t compar) { if (nmemb <= 0) return NULL; @@ -32,14 +38,14 @@ static inline void* bsearch_safe(const void *key, const void *base, ({ \ const typeof(b[0]) *_k = k; \ int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ - bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \ + bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \ }) /** * Normal qsort requires base to be nonnull. Here were require * that only if nmemb > 0. */ -static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { +static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) { if (nmemb <= 1) return; @@ -52,11 +58,11 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_f #define typesafe_qsort(p, n, func) \ ({ \ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ + _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \ }) #if 0 /* NM_IGNORED */ -static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) { +static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) { if (nmemb <= 1) return; @@ -67,7 +73,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_ #define typesafe_qsort_r(p, n, func, userdata) \ ({ \ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \ - qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \ + qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/sparse-endian.h b/src/libnm-systemd-shared/src/basic/sparse-endian.h index 9583dda9..c795d3da 100644 --- a/src/libnm-systemd-shared/src/basic/sparse-endian.h +++ b/src/libnm-systemd-shared/src/basic/sparse-endian.h @@ -55,9 +55,9 @@ typedef uint64_t __sd_bitwise be64_t; #undef le64toh #if __BYTE_ORDER == __LITTLE_ENDIAN -#define bswap_16_on_le(x) __bswap_16(x) -#define bswap_32_on_le(x) __bswap_32(x) -#define bswap_64_on_le(x) __bswap_64(x) +#define bswap_16_on_le(x) bswap_16(x) +#define bswap_32_on_le(x) bswap_32(x) +#define bswap_64_on_le(x) bswap_64(x) #define bswap_16_on_be(x) (x) #define bswap_32_on_be(x) (x) #define bswap_64_on_be(x) (x) @@ -65,9 +65,9 @@ typedef uint64_t __sd_bitwise be64_t; #define bswap_16_on_le(x) (x) #define bswap_32_on_le(x) (x) #define bswap_64_on_le(x) (x) -#define bswap_16_on_be(x) __bswap_16(x) -#define bswap_32_on_be(x) __bswap_32(x) -#define bswap_64_on_be(x) __bswap_64(x) +#define bswap_16_on_be(x) bswap_16(x) +#define bswap_32_on_be(x) bswap_32(x) +#define bswap_64_on_be(x) bswap_64(x) #endif static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); } diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index 133828bc..8126ce08 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -15,10 +15,13 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" +#include "filesystems.h" +#include "fs-util.h" #include "macro.h" #include "missing_fs.h" #include "missing_magic.h" #include "missing_syscall.h" +#include "nulstr-util.h" #include "parse-util.h" #include "stat-util.h" #include "string-util.h" @@ -75,27 +78,41 @@ int is_device_node(const char *path) { int dir_is_empty_at(int dir_fd, const char *path) { _cleanup_close_ int fd = -1; - _cleanup_closedir_ DIR *d = NULL; + /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." + + * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */ + DEFINE_DIRENT_BUFFER(buffer, 3); struct dirent *de; + ssize_t n; if (path) { + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); if (fd < 0) return -errno; + } else if (dir_fd == AT_FDCWD) { + fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (fd < 0) + return -errno; } else { - /* Note that DUPing is not enough, as the internal pointer - * would still be shared and moved by FOREACH_DIRENT. */ - fd = fd_reopen(dir_fd, O_CLOEXEC); + /* Note that DUPing is not enough, as the internal pointer would still be shared and moved + * getedents64(). */ + assert(dir_fd >= 0); + + fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC); if (fd < 0) return fd; } - d = take_fdopendir(&fd); - if (!d) + n = getdents64(fd, &buffer, sizeof(buffer)); + if (n < 0) return -errno; - FOREACH_DIRENT(de, d, return -errno) - return 0; + msan_unpoison(&buffer, n); + + FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n) + if (!dot_or_dot_dot(de->d_name)) + return 0; return 1; } @@ -178,7 +195,6 @@ int files_same(const char *filea, const char *fileb, int flags) { a.st_ino == b.st_ino; } -#if 0 /* NM_IGNORED */ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { assert(s); assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); @@ -186,6 +202,7 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { return F_TYPE_EQUAL(s->f_type, magic_value); } +#if 0 /* NM_IGNORED */ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { struct statfs s; @@ -194,6 +211,7 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { return is_fs_type(&s, magic_value); } +#endif /* NM_IGNORE */ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { struct statfs s; @@ -204,20 +222,13 @@ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { return is_fs_type(&s, magic_value); } +#if 0 /* NM_IGNORE */ bool is_temporary_fs(const struct statfs *s) { - return is_fs_type(s, TMPFS_MAGIC) || - is_fs_type(s, RAMFS_MAGIC); + return fs_in_group(s, FILESYSTEM_SET_TEMPORARY); } bool is_network_fs(const struct statfs *s) { - return is_fs_type(s, CIFS_MAGIC_NUMBER) || - is_fs_type(s, CODA_SUPER_MAGIC) || - is_fs_type(s, NCP_SUPER_MAGIC) || - is_fs_type(s, NFS_SUPER_MAGIC) || - is_fs_type(s, SMB_SUPER_MAGIC) || - is_fs_type(s, V9FS_MAGIC) || - is_fs_type(s, AFS_SUPER_MAGIC) || - is_fs_type(s, OCFS2_SUPER_MAGIC); + return fs_in_group(s, FILESYSTEM_SET_NETWORK); } int fd_is_temporary_fs(int fd) { @@ -395,6 +406,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret return 0; } +#endif /* NM_IGNORED */ int proc_mounted(void) { int r; @@ -408,6 +420,7 @@ int proc_mounted(void) { return r; } +#if 0 /* NM_IGNORED */ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index 775d694c..a1d88fbb 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -60,6 +60,10 @@ static inline const char *empty_to_null(const char *p) { return isempty(p) ? NULL : p; } +static inline const char *empty_to_na(const char *p) { + return isempty(p) ? "n/a" : p; +} + static inline const char *empty_to_dash(const char *str) { return isempty(str) ? "-" : str; } diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index 1d840923..3e0faf95 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -18,7 +18,7 @@ #include "string-util.h" #include "strv.h" -char *strv_find(char * const *l, const char *name) { +char* strv_find(char * const *l, const char *name) { char * const *i; assert(name); @@ -30,7 +30,7 @@ char *strv_find(char * const *l, const char *name) { return NULL; } -char *strv_find_case(char * const *l, const char *name) { +char* strv_find_case(char * const *l, const char *name) { char * const *i; assert(name); @@ -42,7 +42,7 @@ char *strv_find_case(char * const *l, const char *name) { return NULL; } -char *strv_find_prefix(char * const *l, const char *name) { +char* strv_find_prefix(char * const *l, const char *name) { char * const *i; assert(name); @@ -54,7 +54,7 @@ char *strv_find_prefix(char * const *l, const char *name) { return NULL; } -char *strv_find_startswith(char * const *l, const char *name) { +char* strv_find_startswith(char * const *l, const char *name) { char * const *i, *e; assert(name); @@ -71,19 +71,17 @@ char *strv_find_startswith(char * const *l, const char *name) { return NULL; } -char **strv_free(char **l) { - char **k; - +char** strv_free(char **l) { if (!l) return NULL; - for (k = l; *k; k++) + for (char **k = l; *k; k++) free(*k); return mfree(l); } -char **strv_free_erase(char **l) { +char** strv_free_erase(char **l) { char **i; STRV_FOREACH(i, l) @@ -92,7 +90,7 @@ char **strv_free_erase(char **l) { return mfree(l); } -char **strv_copy(char * const *l) { +char** strv_copy(char * const *l) { char **r, **k; k = r = new(char*, strv_length(l) + 1); @@ -124,7 +122,7 @@ size_t strv_length(char * const *l) { return n; } -char **strv_new_ap(const char *x, va_list ap) { +char** strv_new_ap(const char *x, va_list ap) { _cleanup_strv_free_ char **a = NULL; size_t n = 0, i = 0; va_list aq; @@ -163,7 +161,7 @@ char **strv_new_ap(const char *x, va_list ap) { return TAKE_PTR(a); } -char **strv_new_internal(const char *x, ...) { +char** strv_new_internal(const char *x, ...) { char **r; va_list ap; @@ -176,7 +174,7 @@ char **strv_new_internal(const char *x, ...) { int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { char * const *s, **t; - size_t p, q, i = 0, j; + size_t p, q, i = 0; assert(a); @@ -197,7 +195,6 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { *a = t; STRV_FOREACH(s, b) { - if (filter_duplicates && strv_contains(t, *s)) continue; @@ -214,7 +211,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { return (int) i; rollback: - for (j = 0; j < i; j++) + for (size_t j = 0; j < i; j++) free(t[p + j]); t[p] = NULL; @@ -289,7 +286,6 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla return -ENOMEM; l[n++] = TAKE_PTR(word); - l[n] = NULL; } @@ -376,7 +372,7 @@ int strv_split_colon_pairs(char ***t, const char *s) { } #endif /* NM_IGNORED */ -char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { +char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { char * const *s; char *r, *e; size_t n, k, m; @@ -483,7 +479,7 @@ int strv_push_pair(char ***l, char *a, char *b) { int strv_insert(char ***l, size_t position, char *value) { char **c; - size_t n, m, i; + size_t n, m; if (!value) return 0; @@ -500,18 +496,14 @@ int strv_insert(char ***l, size_t position, char *value) { if (!c) return -ENOMEM; - for (i = 0; i < position; i++) + for (size_t i = 0; i < position; i++) c[i] = (*l)[i]; c[position] = value; - for (i = position; i < n; i++) + for (size_t i = position; i < n; i++) c[i+1] = (*l)[i]; - c[n+1] = NULL; - free(*l); - *l = c; - - return 0; + return free_and_replace(*l, c); } int strv_consume(char ***l, char *value) { @@ -608,7 +600,7 @@ int strv_extend_front(char ***l, const char *value) { return 0; } -char **strv_uniq(char **l) { +char** strv_uniq(char **l) { char **i; /* Drops duplicate entries. The first identical string will be @@ -630,7 +622,7 @@ bool strv_is_uniq(char * const *l) { return true; } -char **strv_remove(char **l, const char *s) { +char** strv_remove(char **l, const char *s) { char **f, **t; if (!l) @@ -651,7 +643,7 @@ char **strv_remove(char **l, const char *s) { return l; } -char **strv_parse_nulstr(const char *s, size_t l) { +char** strv_parse_nulstr(const char *s, size_t l) { /* l is the length of the input data, which will be split at NULs into * elements of the resulting strv. Hence, the number of items in the resulting strv * will be equal to one plus the number of NUL bytes in the l bytes starting at s, @@ -663,7 +655,6 @@ char **strv_parse_nulstr(const char *s, size_t l) { * empty strings in s. */ - const char *p; size_t c = 0, i = 0; char **v; @@ -672,7 +663,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { if (l <= 0) return new0(char*, 1); - for (p = s; p < s + l; p++) + for (const char *p = s; p < s + l; p++) if (*p == 0) c++; @@ -683,8 +674,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { if (!v) return NULL; - p = s; - while (p < s + l) { + for (const char *p = s; p < s + l; ) { const char *e; e = memchr(p, 0, s + l - p); @@ -709,7 +699,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { } #if 0 /* NM_IGNORED */ -char **strv_split_nulstr(const char *s) { +char** strv_split_nulstr(const char *s) { const char *i; char **r = NULL; @@ -786,7 +776,7 @@ static int str_compare(char * const *a, char * const *b) { return strcmp(*a, *b); } -char **strv_sort(char **l) { +char** strv_sort(char **l) { typesafe_qsort(l, strv_length(l), str_compare); return l; } @@ -836,21 +826,21 @@ int strv_extendf(char ***l, const char *format, ...) { return strv_consume(l, x); } -char **strv_reverse(char **l) { - size_t n, i; +char** strv_reverse(char **l) { + size_t n; n = strv_length(l); if (n <= 1) return l; - for (i = 0; i < n / 2; i++) + for (size_t i = 0; i < n / 2; i++) SWAP_TWO(l[i], l[n-1-i]); return l; } #if 0 /* NM_IGNORED */ -char **strv_shell_escape(char **l, const char *bad) { +char** strv_shell_escape(char **l, const char *bad) { char **s; /* Escapes every character in every string in l that is in bad, @@ -882,19 +872,7 @@ bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t * return false; } -char ***strv_free_free(char ***l) { - char ***i; - - if (!l) - return NULL; - - for (i = l; *i; i++) - strv_free(*i); - - return mfree(l); -} - -char **strv_skip(char **l, size_t n) { +char** strv_skip(char **l, size_t n) { while (n > 0) { if (strv_isempty(l)) @@ -907,7 +885,7 @@ char **strv_skip(char **l, size_t n) { } int strv_extend_n(char ***l, const char *value, size_t n) { - size_t i, j, k; + size_t i, k; char **nl; assert(l); @@ -934,15 +912,15 @@ int strv_extend_n(char ***l, const char *value, size_t n) { if (!nl[i]) goto rollback; } - nl[i] = NULL; + return 0; rollback: - for (j = k; j < i; j++) + for (size_t j = k; j < i; j++) free(nl[j]); - nl[k] = NULL; + return -ENOMEM; } diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index a56ef941..092d40c8 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -13,23 +13,23 @@ #include "macro.h" #include "string-util.h" -char *strv_find(char * const *l, const char *name) _pure_; -char *strv_find_case(char * const *l, const char *name) _pure_; -char *strv_find_prefix(char * const *l, const char *name) _pure_; -char *strv_find_startswith(char * const *l, const char *name) _pure_; +char* strv_find(char * const *l, const char *name) _pure_; +char* strv_find_case(char * const *l, const char *name) _pure_; +char* strv_find_prefix(char * const *l, const char *name) _pure_; +char* strv_find_startswith(char * const *l, const char *name) _pure_; #define strv_contains(l, s) (!!strv_find((l), (s))) #define strv_contains_case(l, s) (!!strv_find_case((l), (s))) -char **strv_free(char **l); +char** strv_free(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); #define _cleanup_strv_free_ _cleanup_(strv_freep) -char **strv_free_erase(char **l); +char** strv_free_erase(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) -char **strv_copy(char * const *l); +char** strv_copy(char * const *l); size_t strv_length(char * const *l) _pure_; int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); @@ -50,8 +50,8 @@ int strv_consume(char ***l, char *value); int strv_consume_pair(char ***l, char *a, char *b); int strv_consume_prepend(char ***l, char *value); -char **strv_remove(char **l, const char *s); -char **strv_uniq(char **l); +char** strv_remove(char **l, const char *s); +char** strv_uniq(char **l); bool strv_is_uniq(char * const *l); int strv_compare(char * const *a, char * const *b); @@ -59,8 +59,8 @@ static inline bool strv_equal(char * const *a, char * const *b) { return strv_compare(a, b) == 0; } -char **strv_new_internal(const char *x, ...) _sentinel_; -char **strv_new_ap(const char *x, va_list ap); +char** strv_new_internal(const char *x, ...) _sentinel_; +char** strv_new_ap(const char *x, va_list ap); #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) #define STRV_IGNORE ((const char *) POINTER_MAX) @@ -74,7 +74,7 @@ static inline bool strv_isempty(char * const *l) { } int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags); -static inline char **strv_split(const char *s, const char *separators) { +static inline char** strv_split(const char *s, const char *separators) { char **ret; if (strv_split_full(&ret, s, separators, 0) < 0) @@ -87,7 +87,7 @@ int strv_split_and_extend_full(char ***t, const char *s, const char *separators, #define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0) int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags); -static inline char **strv_split_newlines(const char *s) { +static inline char** strv_split_newlines(const char *s) { char **ret; if (strv_split_newlines_full(&ret, s, 0) < 0) @@ -101,13 +101,13 @@ static inline char **strv_split_newlines(const char *s) { * string in the vector is an empty string. */ int strv_split_colon_pairs(char ***t, const char *s); -char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); +char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); static inline char *strv_join(char * const *l, const char *separator) { return strv_join_full(l, separator, NULL, false); } -char **strv_parse_nulstr(const char *s, size_t l); -char **strv_split_nulstr(const char *s); +char** strv_parse_nulstr(const char *s, size_t l); +char** strv_split_nulstr(const char *s); int strv_make_nulstr(char * const *l, char **p, size_t *n); static inline int strv_from_nulstr(char ***a, const char *nulstr) { @@ -136,7 +136,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; #define STRV_FOREACH_PAIR(x, y, l) \ for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) -char **strv_sort(char **l); +char** strv_sort(char **l); void strv_print(char * const *l); #define strv_from_stdarg_alloca(first) \ @@ -206,13 +206,16 @@ void strv_print(char * const *l); _found; \ }) -#define FOREACH_STRING(x, y, ...) \ - for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ +#define _FOREACH_STRING(uniq, x, y, ...) \ + for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ x; \ - x = *(++_l)) + x = *(++UNIQ_T(l, uniq))) -char **strv_reverse(char **l); -char **strv_shell_escape(char **l, const char *bad); +#define FOREACH_STRING(x, y, ...) \ + _FOREACH_STRING(UNIQ, x, y, ##__VA_ARGS__) + +char** strv_reverse(char **l); +char** strv_shell_escape(char **l, const char *bad); bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos); static inline bool strv_fnmatch(char* const* patterns, const char *s) { @@ -225,10 +228,7 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i strv_fnmatch_full(patterns, s, flags, NULL); } -char ***strv_free_free(char ***l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free); - -char **strv_skip(char **l, size_t n); +char** strv_skip(char **l, size_t n); int strv_extend_n(char ***l, const char *value, size_t n); diff --git a/src/libnm-systemd-shared/src/basic/strxcpyx.c b/src/libnm-systemd-shared/src/basic/strxcpyx.c index 39aebb88..2788dd82 100644 --- a/src/libnm-systemd-shared/src/basic/strxcpyx.c +++ b/src/libnm-systemd-shared/src/basic/strxcpyx.c @@ -17,57 +17,73 @@ #include <stdio.h> #include <string.h> +#include "string-util.h" #include "strxcpyx.h" -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { +size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) { + bool truncated = false; + assert(dest); assert(src); - if (size == 0) + if (size == 0) { + if (ret_truncated) + *ret_truncated = len > 0; return 0; + } if (len >= size) { if (size > 1) *dest = mempcpy(*dest, src, size-1); size = 0; + truncated = true; } else if (len > 0) { *dest = mempcpy(*dest, src, len); size -= len; } + if (ret_truncated) + *ret_truncated = truncated; + *dest[0] = '\0'; return size; } -size_t strpcpy(char **dest, size_t size, const char *src) { +size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) { assert(dest); assert(src); - return strnpcpy(dest, size, src, strlen(src)); + return strnpcpy_full(dest, size, src, strlen(src), ret_truncated); } -size_t strpcpyf(char **dest, size_t size, const char *src, ...) { +size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; int i; assert(dest); assert(src); - if (size == 0) - return 0; - va_start(va, src); i = vsnprintf(*dest, size, src, va); - if (i < (int)size) { + va_end(va); + + if (i < (int) size) { *dest += i; size -= i; - } else + } else { size = 0; - va_end(va); + truncated = i > 0; + } + + if (ret_truncated) + *ret_truncated = truncated; + return size; } -size_t strpcpyl(char **dest, size_t size, const char *src, ...) { +size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; assert(dest); @@ -75,31 +91,38 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) { va_start(va, src); do { - size = strpcpy(dest, size, src); + bool t; + + size = strpcpy_full(dest, size, src, &t); + truncated = truncated || t; src = va_arg(va, char *); } while (src); va_end(va); + + if (ret_truncated) + *ret_truncated = truncated; return size; } -size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { +size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) { char *s; assert(dest); assert(src); s = dest; - return strnpcpy(&s, size, src, len); + return strnpcpy_full(&s, size, src, len, ret_truncated); } -size_t strscpy(char *dest, size_t size, const char *src) { +size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) { assert(dest); assert(src); - return strnscpy(dest, size, src, strlen(src)); + return strnscpy_full(dest, size, src, strlen(src), ret_truncated); } -size_t strscpyl(char *dest, size_t size, const char *src, ...) { +size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; char *s; @@ -109,10 +132,16 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) { va_start(va, src); s = dest; do { - size = strpcpy(&s, size, src); + bool t; + + size = strpcpy_full(&s, size, src, &t); + truncated = truncated || t; src = va_arg(va, char *); } while (src); va_end(va); + if (ret_truncated) + *ret_truncated = truncated; + return size; } diff --git a/src/libnm-systemd-shared/src/basic/strxcpyx.h b/src/libnm-systemd-shared/src/basic/strxcpyx.h index cdef492d..4a648ed0 100644 --- a/src/libnm-systemd-shared/src/basic/strxcpyx.h +++ b/src/libnm-systemd-shared/src/basic/strxcpyx.h @@ -1,14 +1,33 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <stdbool.h> #include <stddef.h> #include "macro.h" -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len); -size_t strpcpy(char **dest, size_t size, const char *src); -size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); -size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; -size_t strnscpy(char *dest, size_t size, const char *src, size_t len); -size_t strscpy(char *dest, size_t size, const char *src); -size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; +size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated); +static inline size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { + return strnpcpy_full(dest, size, src, len, NULL); +} +size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated); +static inline size_t strpcpy(char **dest, size_t size, const char *src) { + return strpcpy_full(dest, size, src, NULL); +} +size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _printf_(4, 5); +#define strpcpyf(dest, size, src, ...) \ + strpcpyf_full((dest), (size), NULL, (src), ##__VA_ARGS__) +size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_; +#define strpcpyl(dest, size, src, ...) \ + strpcpyl_full((dest), (size), NULL, (src), ##__VA_ARGS__) +size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated); +static inline size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { + return strnscpy_full(dest, size, src, len, NULL); +} +size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated); +static inline size_t strscpy(char *dest, size_t size, const char *src) { + return strscpy_full(dest, size, src, NULL); +} +size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_; +#define strscpyl(dest, size, src, ...) \ + strscpyl_full(dest, size, NULL, src, ##__VA_ARGS__) diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index 21e7b24a..d7ea2b38 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -271,7 +271,6 @@ struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) { return ts; } -#if 0 /* NM_IGNORED */ usec_t timeval_load(const struct timeval *tv) { assert(tv); @@ -286,6 +285,7 @@ usec_t timeval_load(const struct timeval *tv) { (usec_t) tv->tv_usec; } +#if 0 /* NM_IGNORED */ struct timeval *timeval_store(struct timeval *tv, usec_t u) { assert(tv); @@ -676,7 +676,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { goto finish; } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); + t = strndupa_safe(t, k - t); r = parse_sec(t, &minus); if (r < 0) @@ -685,7 +685,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { goto finish; } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); + t = strndupa_safe(t, k - t); r = parse_sec(t, &plus); if (r < 0) @@ -697,7 +697,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { /* See if the timestamp is suffixed with UTC */ utc = endswith_no_case(t, " UTC"); if (utc) - t = strndupa(t, utc - t); + t = strndupa_safe(t, utc - t); else { const char *e = NULL; int j; @@ -728,7 +728,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { if (IN_SET(j, 0, 1)) { /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); + t = strndupa_safe(t, e - t - 1); dst = j; tzn = tzname[j]; } @@ -929,7 +929,7 @@ int parse_timestamp(const char *t, usec_t *usec) { /* Cut off the timezone if we don't need it. */ if (with_tz) - t = strndupa(t, last_space - t); + t = strndupa_safe(t, last_space - t); shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index 8a6404d3..dc5a49e2 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -67,16 +67,9 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { /* This is much like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern) { - int fd = -1; /* avoid false maybe-uninitialized warning */ - assert(pattern); - - RUN_WITH_UMASK(0077) - fd = mkostemp(pattern, O_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; + BLOCK_WITH_UMASK(0077); + return RET_NERRNO(mkostemp(pattern, O_CLOEXEC)); } #if 0 /* NM_IGNORED */ @@ -288,8 +281,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { } int link_tmpfile(int fd, const char *path, const char *target) { - int r; - assert(fd >= 0); assert(target); @@ -300,16 +291,10 @@ int link_tmpfile(int fd, const char *path, const char *target) { * 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 { - if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return -errno; - } + if (path) + return rename_noreplace(AT_FDCWD, path, AT_FDCWD, target); - return 0; + return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW)); } int mkdtemp_malloc(const char *template, char **ret) { diff --git a/src/libnm-systemd-shared/src/basic/umask-util.h b/src/libnm-systemd-shared/src/basic/umask-util.h index bd7c2bdb..90d18f70 100644 --- a/src/libnm-systemd-shared/src/basic/umask-util.h +++ b/src/libnm-systemd-shared/src/basic/umask-util.h @@ -24,3 +24,6 @@ assert_cc((S_IFMT & 0777) == 0); for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \ FLAGS_SET(_saved_umask_, S_IFMT); \ _saved_umask_ &= 0777) + +#define BLOCK_WITH_UMASK(mask) \ + _unused_ _cleanup_umask_ mode_t _saved_umask_ = umask(mask); diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c index dfa010e8..c8e39fe4 100644 --- a/src/libnm-systemd-shared/src/basic/utf8.c +++ b/src/libnm-systemd-shared/src/basic/utf8.c @@ -126,11 +126,9 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { } bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) { - const char *p; - assert(str); - for (p = str; length > 0;) { + for (const char *p = str; length > 0;) { int encoded_len, r; char32_t val; @@ -293,14 +291,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool #endif /* NM_IGNORED */ char *ascii_is_valid(const char *str) { - const char *p; - /* Check whether the string consists of valid ASCII bytes, * i.e values between 0 and 127, inclusive. */ assert(str); - for (p = str; *p; p++) + for (const char *p = str; *p; p++) if ((unsigned char) *p >= 128) return NULL; @@ -322,6 +318,37 @@ char *ascii_is_valid_n(const char *str, size_t len) { } #endif /* NM_IGNORED */ +int utf8_to_ascii(const char *str, char replacement_char, char **ret) { + /* Convert to a string that has only ASCII chars, replacing anything that is not ASCII + * by replacement_char. */ + + _cleanup_free_ char *ans = new(char, strlen(str) + 1); + if (!ans) + return -ENOMEM; + + char *q = ans; + + for (const char *p = str; *p; q++) { + int l; + + l = utf8_encoded_valid_unichar(p, SIZE_MAX); + if (l < 0) /* Non-UTF-8, let's not even try to propagate the garbage */ + return l; + + if (l == 1) + *q = *p; + else + /* non-ASCII, we need to replace it */ + *q = replacement_char; + + p += l; + } + *q = '\0'; + + *ret = TAKE_PTR(ans); + return 0; +} + /** * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 * @out_utf8: output buffer of at least 4 bytes or NULL diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h index b0e969f6..4a06dd62 100644 --- a/src/libnm-systemd-shared/src/basic/utf8.h +++ b/src/libnm-systemd-shared/src/basic/utf8.h @@ -21,6 +21,8 @@ static inline char *utf8_is_valid(const char *s) { char *ascii_is_valid(const char *s) _pure_; char *ascii_is_valid_n(const char *str, size_t len); +int utf8_to_ascii(const char *str, char replacement_char, char **ret); + bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_; #define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) diff --git a/src/libnm-systemd-shared/src/basic/util.c b/src/libnm-systemd-shared/src/basic/util.c index ed328466..3425d0f0 100644 --- a/src/libnm-systemd-shared/src/basic/util.c +++ b/src/libnm-systemd-shared/src/basic/util.c @@ -8,7 +8,6 @@ #include "alloc-util.h" #include "build.h" -#include "dirent-util.h" #include "env-file.h" #include "env-util.h" #include "fd-util.h" @@ -118,71 +117,6 @@ void in_initrd_force(bool value) { saved_in_initrd = value; } -int on_ac_power(void) { - bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; - - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_close_ int fd = -1, device = -1; - char contents[6]; - ssize_t n; - - device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (device < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR)) - continue; - - return -errno; - } - - fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 6 || memcmp(contents, "Mains\n", 6)) - continue; - - safe_close(fd); - fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 2 || contents[1] != '\n') - return -EIO; - - if (contents[0] == '1') { - found_online = true; - break; - } else if (contents[0] == '0') - found_offline = true; - else - return -EIO; - } - - return found_online || !found_offline; -} - int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; const char *p; diff --git a/src/libnm-systemd-shared/src/basic/util.h b/src/libnm-systemd-shared/src/basic/util.h index b6c51c03..dd1cb4f3 100644 --- a/src/libnm-systemd-shared/src/basic/util.h +++ b/src/libnm-systemd-shared/src/basic/util.h @@ -20,13 +20,24 @@ int prot_from_flags(int flags) _const_; bool in_initrd(void); void in_initrd_force(bool value); -int on_ac_power(void); - -static inline unsigned u64log2(uint64_t n) { +/* Note: log2(0) == log2(1) == 0 here and below. */ + +#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0) +#define NONCONST_LOG2ULL(x) ({ \ + unsigned long long _x = (x); \ + _x > 1 ? (unsigned) __builtin_clzll(_x) ^ 63U : 0; \ + }) +#if 0 /* NM_IGNORED */ +#define LOG2ULL(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2ULL(x), NONCONST_LOG2ULL(x)) +#else /* NM_IGNORED */ +#define LOG2ULL(x) NONCONST_LOG2ULL(x) +#endif /* NM_IGNORED */ + +static inline unsigned log2u64(uint64_t x) { #if __SIZEOF_LONG_LONG__ == 8 - return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; + return LOG2ULL(x); #else -#error "Wut?" +# error "Wut?" #endif } @@ -34,26 +45,31 @@ static inline unsigned u32ctz(uint32_t n) { #if __SIZEOF_INT__ == 4 return n != 0 ? __builtin_ctz(n) : 32; #else -#error "Wut?" +# error "Wut?" #endif } -static inline unsigned log2i(int x) { - assert(x > 0); +#define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0) +#define NONCONST_LOG2U(x) ({ \ + unsigned _x = (x); \ + _x > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(_x) - 1 : 0; \ + }) +#if 0 /* NM_IGNORED */ +#define LOG2U(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2U(x), NONCONST_LOG2U(x)) +#else /* NM_IGNORED */ +#define LOG2U(x) NONCONST_LOG2U(x) +#endif /* NM_IGNORED */ - return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; +static inline unsigned log2i(int x) { + return LOG2U(x); } static inline unsigned log2u(unsigned x) { - assert(x > 0); - - return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; + return LOG2U(x); } static inline unsigned log2u_round_up(unsigned x) { - assert(x > 0); - - if (x == 1) + if (x <= 1) return 0; return log2u(x - 1) + 1; diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index 20d8dabf..d597c743 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -5,13 +5,15 @@ #include <assert.h> #endif -#include "type.h" +#include <limits.h> +#include "types-fundamental.h" #define _align_(x) __attribute__((__aligned__(x))) #define _const_ __attribute__((__const__)) #define _pure_ __attribute__((__pure__)) #define _section_(x) __attribute__((__section__(x))) #define _packed_ __attribute__((__packed__)) +#define _retain_ __attribute__((__retain__)) #define _used_ __attribute__((__used__)) #define _unused_ __attribute__((__unused__)) #define _cleanup_(x) __attribute__((__cleanup__(x))) @@ -59,8 +61,19 @@ #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__) #endif + + #define memcpy(a, b, c) CopyMem((a), (b), (c)) + #define free(a) FreePool(a) #endif +/* This passes the argument through after (if asserts are enabled) checking that it is not null. */ +#define ASSERT_PTR(expr) \ + ({ \ + typeof(expr) _expr_ = (expr); \ + assert(_expr_); \ + _expr_; \ + }) + #if defined(static_assert) #define assert_cc(expr) \ static_assert(expr, #expr) @@ -81,8 +94,8 @@ #define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) #define __ONCE(o) \ ({ \ - static bool (o) = false; \ - __sync_bool_compare_and_swap(&(o), false, true); \ + static sd_bool (o) = sd_false; \ + __sync_bool_compare_and_swap(&(o), sd_false, sd_true); \ }) #undef MAX @@ -232,7 +245,7 @@ #define IN_SET(x, ...) \ ({ \ - sd_bool _found = false; \ + sd_bool _found = sd_false; \ /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ @@ -241,7 +254,7 @@ assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ switch(x) { \ FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = true; \ + _found = sd_true; \ break; \ default: \ break; \ @@ -253,8 +266,9 @@ * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ #define TAKE_PTR(ptr) \ ({ \ - typeof(ptr) _ptr_ = (ptr); \ - (ptr) = NULL; \ + typeof(ptr) *_pptr_ = &(ptr); \ + typeof(ptr) _ptr_ = *_pptr_; \ + *_pptr_ = NULL; \ _ptr_; \ }) @@ -264,3 +278,47 @@ * @x: a string literal. */ #define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0]))) + +#define mfree(memory) \ + ({ \ + free(memory); \ + (typeof(memory)) NULL; \ + }) + +static inline size_t ALIGN_TO(size_t l, size_t ali) { + /* sd-boot uses UINTN for size_t, let's make sure SIZE_MAX is correct. */ + assert_cc(SIZE_MAX == ~(size_t)0); + + /* Check that alignment is exponent of 2 */ +#if SIZE_MAX == UINT_MAX + assert(__builtin_popcount(ali) == 1); +#elif SIZE_MAX == ULONG_MAX + assert(__builtin_popcountl(ali) == 1); +#elif SIZE_MAX == ULLONG_MAX + assert(__builtin_popcountll(ali) == 1); +#else + #error "Unexpected size_t" +#endif + + if (l > SIZE_MAX - (ali - 1)) + return SIZE_MAX; /* indicate overflow */ + + return ((l + ali - 1) & ~(ali - 1)); +} + +/* Same as ALIGN_TO but callable in constant contexts. */ +#define CONST_ALIGN_TO(l, ali) \ + __builtin_choose_expr( \ + __builtin_constant_p(l) && \ + __builtin_constant_p(ali) && \ + __builtin_popcountll(ali) == 1 && /* is power of 2? */ \ + (l <= SIZE_MAX - (ali - 1)), /* overflow? */ \ + ((l) + (ali) - 1) & ~((ali) - 1), \ + VOID_0) + +#define UPDATE_FLAG(orig, flag, b) \ + ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) +#define SET_FLAG(v, flag, b) \ + (v) = UPDATE_FLAG(v, flag, b) +#define FLAGS_SET(v, flags) \ + ((~(v) & (flags)) == 0) diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c index f1703926..73569765 100644 --- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c @@ -10,7 +10,7 @@ #include "string-util-fundamental.h" sd_char *startswith(const sd_char *s, const sd_char *prefix) { - sd_size_t l; + size_t l; assert(s); assert(prefix); @@ -24,7 +24,7 @@ sd_char *startswith(const sd_char *s, const sd_char *prefix) { #ifndef SD_BOOT sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) { - sd_size_t l; + size_t l; assert(s); assert(prefix); @@ -38,7 +38,7 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) { #endif sd_char* endswith(const sd_char *s, const sd_char *postfix) { - sd_size_t sl, pl; + size_t sl, pl; assert(s); assert(postfix); @@ -59,7 +59,7 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) { } sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) { - sd_size_t sl, pl; + size_t sl, pl; assert(s); assert(postfix); diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h index 7455c054..dc0c1202 100644 --- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h @@ -68,10 +68,10 @@ static inline const sd_char *yes_no(sd_bool b) { sd_int strverscmp_improved(const sd_char *a, const sd_char *b); /* Like startswith(), but operates on arbitrary memory blocks */ -static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) { +static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) { assert(token); - sd_size_t n = strlen(token) * sizeof(sd_char); + size_t n = strlen(token) * sizeof(sd_char); if (sz < n) return NULL; diff --git a/src/libnm-systemd-shared/src/fundamental/type.h b/src/libnm-systemd-shared/src/fundamental/type.h deleted file mode 100644 index f645d2de..00000000 --- a/src/libnm-systemd-shared/src/fundamental/type.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#ifdef SD_BOOT -#include <efi.h> - -typedef BOOLEAN sd_bool; -typedef CHAR16 sd_char; -typedef INTN sd_int; -typedef UINTN sd_size_t; - -#define true TRUE -#define false FALSE -#else -#include <stdbool.h> -#include <stdint.h> - -typedef bool sd_bool; -typedef char sd_char; -typedef int sd_int; -typedef size_t sd_size_t; -#endif diff --git a/src/libnm-systemd-shared/src/fundamental/types-fundamental.h b/src/libnm-systemd-shared/src/fundamental/types-fundamental.h new file mode 100644 index 00000000..5977e40c --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/types-fundamental.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment, + * but mostly the same in concept and behaviour. + * + * Note: if the definition of these types/values has slightly different semantics in userspace and in the + * UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics, + * and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace + * and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI + * space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100% + * defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI + * environment, so that size_t can be used everywhere, without any "sd_" prefix.) + * + * Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types + * after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */ + +#ifdef SD_BOOT +#include <efi.h> + +typedef BOOLEAN sd_bool; +typedef CHAR16 sd_char; +typedef INTN sd_int; +typedef UINTN size_t; + +#define sd_true TRUE +#define sd_false FALSE +#else +#include <stdbool.h> +#include <stdint.h> + +typedef bool sd_bool; +typedef char sd_char; +typedef int sd_int; + +#define sd_true true +#define sd_false false + +#endif diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c index 473f6a02..b82a9a1c 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.c +++ b/src/libnm-systemd-shared/src/shared/dns-domain.c @@ -687,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); + prefix = strndupa_safe(name, saved_before - name); r = dns_name_concat(prefix, new_suffix, 0, ret); if (r < 0) @@ -1037,7 +1037,7 @@ static bool dns_service_name_label_is_valid(const char *label, size_t n) { if (memchr(label, 0, n)) return false; - s = strndupa(label, n); + s = strndupa_safe(label, n); return dns_service_name_is_valid(s); } diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h index d0195c58..c237f761 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.h +++ b/src/libnm-systemd-shared/src/shared/dns-domain.h @@ -62,6 +62,10 @@ static inline int dns_name_is_valid_ldh(const char *s) { return 1; } +static inline bool dns_name_is_empty(const char *s) { + return isempty(s) || streq(s, "."); +} + 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; |