diff options
| author | Michael Biebl <biebl@debian.org> | 2023-02-10 11:50:34 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2023-02-10 11:50:34 +0100 |
| commit | 1372848511cb896b80b51ed1a3e9606bd9816631 (patch) | |
| tree | 674792b9385bdef935988894b45f06b2af39f88c /src/libnm-systemd-shared | |
| parent | 40ec077ea305994c1fc2130add6787ca0c73e2c6 (diff) | |
New upstream version 1.42.0 upstream/1.42.0
Diffstat (limited to 'src/libnm-systemd-shared')
40 files changed, 658 insertions, 301 deletions
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/user-util.h b/src/libnm-systemd-shared/sd-adapt-shared/user-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/libnm-systemd-shared/sd-adapt-shared/user-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index 4c413a8d..df6d5b7b 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -86,6 +86,7 @@ bool cpu_accounting_is_cheap(void); /* Special values for all weight knobs on unified hierarchy */ #define CGROUP_WEIGHT_INVALID UINT64_MAX +#define CGROUP_WEIGHT_IDLE UINT64_C(0) #define CGROUP_WEIGHT_MIN UINT64_C(1) #define CGROUP_WEIGHT_MAX UINT64_C(10000) #define CGROUP_WEIGHT_DEFAULT UINT64_C(100) diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c index 61a9cd60..266e4e79 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.c +++ b/src/libnm-systemd-shared/src/basic/env-file.c @@ -446,11 +446,9 @@ static int merge_env_file_push( const char *key, char *value, void *userdata) { - char ***env = userdata; + char ***env = ASSERT_PTR(userdata); char *expanded_value; - assert(env); - if (!value) { log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); return 0; diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index e869fad1..f58d86a7 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -781,6 +781,18 @@ int getenv_bool_secure(const char *p) { } #if 0 /* NM_IGNORED */ +int getenv_uint64_secure(const char *p, uint64_t *ret) { + const char *e; + + assert(p); + + e = secure_getenv(p); + if (!e) + return -ENXIO; + + return safe_atou64(e, ret); +} + int set_unset_env(const char *name, const char *value, bool overwrite) { assert(name); diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h index 2bf0603f..b927ac7a 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.h +++ b/src/libnm-systemd-shared/src/basic/env-util.h @@ -57,6 +57,8 @@ char *strv_env_pairs_get(char **l, const char *name) _pure_; int getenv_bool(const char *p); int getenv_bool_secure(const char *p); +int getenv_uint64_secure(const char *p, uint64_t *ret); + /* Like setenv, but calls unsetenv if value == NULL. */ int set_unset_env(const char *name, const char *value, bool overwrite); diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h index 648de50e..091f99c5 100644 --- a/src/libnm-systemd-shared/src/basic/errno-util.h +++ b/src/libnm-systemd-shared/src/basic/errno-util.h @@ -6,6 +6,21 @@ #include "macro.h" +/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */ +#define ERRNO_BUF_LEN 1024 + +/* 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 + * + * Note that we use the GNU variant of strerror_r() here. */ +#define STRERROR(errnum) strerror_r(abs(errnum), (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN) + +/* A helper to print an error message or message for functions that return 0 on EOF. + * Note that we can't use ({ … }) to define a temporary variable, so errnum is + * evaluated twice. */ +#define STRERROR_OR_EOF(errnum) ((errnum) != 0 ? STRERROR(errnum) : "Unexpected EOF") + static inline void _reset_errno_(int *saved_errno) { if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ return; @@ -22,6 +37,10 @@ static inline void _reset_errno_(int *saved_errno) { _saved_errno_ = -1; \ } while (false) +#define LOCAL_ERRNO(value) \ + PROTECT_ERRNO; \ + errno = abs(value) + static inline int negative_errno(void) { /* This helper should be used to shut up gcc if you know 'errno' is * negative. Instead of "return -errno;", use "return negative_errno();" @@ -54,11 +73,6 @@ static inline int RET_NERRNO(int ret) { 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] */ -} - static inline int errno_or_else(int fallback) { /* To be used when invoking library calls where errno handling is not defined clearly: we return * errno if it is set, and the specified error otherwise. The idea is that the caller initializes @@ -153,3 +167,10 @@ static inline bool ERRNO_IS_DEVICE_ABSENT(int r) { ENXIO, ENOENT); } + +/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and + * where it simply doesn't have the requested xattr the same way */ +static inline bool ERRNO_IS_XATTR_ABSENT(int r) { + return abs(r) == ENODATA || + ERRNO_IS_NOT_SUPPORTED(r); +} diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index 80295445..61a5bb78 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -4,7 +4,6 @@ #include <errno.h> #include <fcntl.h> -#include <linux/btrfs.h> #if WANT_LINUX_FS_H #include <linux/fs.h> #endif @@ -480,7 +479,7 @@ void cmsg_close_all(struct msghdr *mh) { CMSG_FOREACH(cmsg, mh) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); + close_many((int*) ((void*) CMSG_DATA(cmsg)), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); } bool fdname_is_valid(const char *s) { @@ -661,7 +660,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ goto finish; } - CLOSE_AND_REPLACE(null_fd, copy); + close_and_replace(null_fd, copy); } } @@ -786,20 +785,6 @@ int read_nr_open(void) { return 1024 * 1024; } -/* This is here because it's fd-related and is called from sd-journal code. Other btrfs-related utilities are - * in src/shared, but libsystemd must not link to libsystemd-shared, see docs/ARCHITECTURE.md. */ -int btrfs_defrag_fd(int fd) { - int r; - - assert(fd >= 0); - - r = fd_verify_regular(fd); - if (r < 0) - return r; - - return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL)); -} - int fd_get_diskseq(int fd, uint64_t *ret) { uint64_t diskseq; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index 808cac4d..d9896e27 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -98,7 +98,7 @@ static inline int make_null_stdio(void) { }) /* Like free_and_replace(), but for file descriptors */ -#define CLOSE_AND_REPLACE(a, b) \ +#define close_and_replace(a, b) \ ({ \ int *_fdp_ = &(a); \ safe_close(*_fdp_); \ @@ -108,7 +108,6 @@ static inline int make_null_stdio(void) { int fd_reopen(int fd, int flags); int read_nr_open(void); -int btrfs_defrag_fd(int fd); int fd_get_diskseq(int fd, uint64_t *ret); /* The maximum length a buffer for a /proc/self/fd/<fd> path needs */ diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index 1a951bd5..23f8d403 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -403,10 +403,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi return ret; } -int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); -} - int symlink_idempotent(const char *from, const char *to, bool make_relative) { _cleanup_free_ char *relpath = NULL; int r; @@ -415,13 +411,7 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) { assert(to); if (make_relative) { - _cleanup_free_ char *parent = NULL; - - r = path_extract_directory(to, &parent); - if (r < 0) - return r; - - r = path_make_relative(parent, from, &relpath); + r = path_make_relative_parent(to, from, &relpath); if (r < 0) return r; @@ -447,50 +437,38 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) { return 0; } -int symlink_atomic(const char *from, const char *to) { - _cleanup_free_ char *t = NULL; +int symlinkat_atomic_full(const char *from, int atfd, const char *to, bool make_relative) { + _cleanup_free_ char *relpath = NULL, *t = NULL; int r; assert(from); assert(to); - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - if (symlink(from, t) < 0) - return -errno; + if (make_relative) { + r = path_make_relative_parent(to, from, &relpath); + if (r < 0) + return r; - if (rename(t, to) < 0) { - unlink_noerrno(t); - return -errno; + from = relpath; } - return 0; -} - -int mknod_atomic(const char *path, mode_t mode, dev_t dev) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); + r = tempfn_random(to, NULL, &t); if (r < 0) return r; - if (mknod(t, mode, dev) < 0) + if (symlinkat(from, atfd, t) < 0) return -errno; - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; + r = RET_NERRNO(renameat(atfd, t, atfd, to)); + if (r < 0) { + (void) unlinkat(atfd, t, 0); + return r; } return 0; } -int mkfifo_atomic(const char *path, mode_t mode) { +int mknodat_atomic(int atfd, const char *path, mode_t mode, dev_t dev) { _cleanup_free_ char *t = NULL; int r; @@ -500,37 +478,36 @@ int mkfifo_atomic(const char *path, mode_t mode) { if (r < 0) return r; - if (mkfifo(t, mode) < 0) + if (mknodat(atfd, t, mode, dev) < 0) return -errno; - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; + r = RET_NERRNO(renameat(atfd, t, atfd, path)); + if (r < 0) { + (void) unlinkat(atfd, t, 0); + return r; } return 0; } -int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { +int mkfifoat_atomic(int atfd, const char *path, mode_t mode) { _cleanup_free_ char *t = NULL; int r; assert(path); - if (path_is_absolute(path)) - return mkfifo_atomic(path, mode); - /* We're only interested in the (random) filename. */ - r = tempfn_random_child("", NULL, &t); + r = tempfn_random(path, NULL, &t); if (r < 0) return r; - if (mkfifoat(dirfd, t, mode) < 0) + if (mkfifoat(atfd, t, mode) < 0) return -errno; - if (renameat(dirfd, t, dirfd, path) < 0) { - unlink_noerrno(t); - return -errno; + r = RET_NERRNO(renameat(atfd, t, atfd, path)); + if (r < 0) { + (void) unlinkat(atfd, t, 0); + return r; } return 0; diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index e48cf680..c4dffc48 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -13,6 +13,7 @@ #include "alloc-util.h" #include "errno-util.h" #include "time-util.h" +#include "user-util.h" #define MODE_INVALID ((mode_t) -1) @@ -50,14 +51,27 @@ int stat_warn_permissions(const char *path, const struct stat *st); 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); + +static inline int touch(const char *path) { + return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); +} int symlink_idempotent(const char *from, const char *to, bool make_relative); -int symlink_atomic(const char *from, const char *to); -int mknod_atomic(const char *path, mode_t mode, dev_t dev); -int mkfifo_atomic(const char *path, mode_t mode); +int symlinkat_atomic_full(const char *from, int atfd, const char *to, bool make_relative); +static inline int symlink_atomic(const char *from, const char *to) { + return symlinkat_atomic_full(from, AT_FDCWD, to, false); +} + +int mknodat_atomic(int atfd, const char *path, mode_t mode, dev_t dev); +static inline int mknod_atomic(const char *path, mode_t mode, dev_t dev) { + return mknodat_atomic(AT_FDCWD, path, mode, dev); +} + int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode); +static inline int mkfifo_atomic(const char *path, mode_t mode) { + return mkfifoat_atomic(AT_FDCWD, path, mode); +} int get_files_in_directory(const char *path, char ***list); diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c index ec851e8b..5289cffe 100644 --- a/src/libnm-systemd-shared/src/basic/glyph-util.c +++ b/src/libnm-systemd-shared/src/basic/glyph-util.c @@ -55,6 +55,7 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_LIGHT_SHADE] = "-", [SPECIAL_GLYPH_DARK_SHADE] = "X", [SPECIAL_GLYPH_SIGMA] = "S", + [SPECIAL_GLYPH_ARROW_LEFT] = "<-", [SPECIAL_GLYPH_ARROW_RIGHT] = "->", [SPECIAL_GLYPH_ARROW_UP] = "^", [SPECIAL_GLYPH_ARROW_DOWN] = "v", @@ -101,6 +102,7 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */ /* Single glyph in Unicode, two in ASCII */ + [SPECIAL_GLYPH_ARROW_LEFT] = u8"←", /* actually called: LEFTWARDS ARROW */ [SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */ /* Single glyph in Unicode, three in ASCII */ diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h index 065dde8a..621d7a85 100644 --- a/src/libnm-systemd-shared/src/basic/glyph-util.h +++ b/src/libnm-systemd-shared/src/basic/glyph-util.h @@ -22,6 +22,7 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_MU, SPECIAL_GLYPH_CHECK_MARK, SPECIAL_GLYPH_CROSS_MARK, + SPECIAL_GLYPH_ARROW_LEFT, SPECIAL_GLYPH_ARROW_RIGHT, SPECIAL_GLYPH_ARROW_UP, SPECIAL_GLYPH_ARROW_DOWN, diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index 5d0ae6aa..d0b950e0 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -374,7 +374,7 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { } static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) + return (struct hashmap_base_entry*) (void *) ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); } @@ -1192,7 +1192,7 @@ static int resize_buckets(HashmapBase *h, unsigned entries_add) { } while (rehash_next); } - assert(n_rehashed == n_entries(h)); + assert_se(n_rehashed == n_entries(h)); return 1; } @@ -1886,11 +1886,10 @@ int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HA } int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { - const char *p = v; + const char *p = ASSERT_PTR(v); int r; assert(s); - assert(v); for (;;) { char *word; @@ -2083,6 +2082,8 @@ static bool set_fnmatch_one(Set *patterns, const char *needle) { assert(needle); + /* Any failure of fnmatch() is treated as equivalent to FNM_NOMATCH, i.e. as non-matching pattern */ + SET_FOREACH(p, patterns) if (fnmatch(p, needle, 0) == 0) return true; 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 dc4446ae..b25c803b 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -68,7 +68,7 @@ bool in4_addr_is_link_local_dynamic(const struct in_addr *a) { bool in6_addr_is_link_local(const struct in6_addr *a) { assert(a); - return IN6_IS_ADDR_LINKLOCAL(a); /* lgtm [cpp/potentially-dangerous-function] */ + return IN6_IS_ADDR_LINKLOCAL(a); } int in_addr_is_link_local(int family, const union in_addr_union *u) { @@ -133,7 +133,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) { return in4_addr_is_localhost(&u->in); if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + return IN6_IS_ADDR_LOOPBACK(&u->in6); return -EAFNOSUPPORT; } @@ -146,7 +146,7 @@ int in_addr_is_localhost_one(int family, const union in_addr_union *u) { return be32toh(u->in.s_addr) == UINT32_C(0x7F000001); if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + return IN6_IS_ADDR_LOOPBACK(&u->in6); return -EAFNOSUPPORT; } @@ -197,8 +197,7 @@ int in_addr_prefix_intersect( assert(a); assert(b); - /* Checks whether there are any addresses that are in both - * networks */ + /* Checks whether there are any addresses that are in both networks */ m = MIN(aprefixlen, bprefixlen); @@ -206,7 +205,7 @@ int in_addr_prefix_intersect( uint32_t x, nm; x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); + nm = m == 0 ? 0 : 0xFFFFFFFFUL << (32 - m); return (x & nm) == 0; } @@ -594,6 +593,7 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { return 32U - u32ctz(be32toh(addr->s_addr)); } +/* Calculate an IPv4 netmask from prefix length, for example /8 -> 255.0.0.0. */ struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { assert(addr); assert(prefixlen <= 32); @@ -607,6 +607,47 @@ struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned cha return addr; } +/* Calculate an IPv6 netmask from prefix length, for example /16 -> ffff::. */ +struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen) { + assert(addr); + assert(prefixlen <= 128); + + for (unsigned 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; + } + + addr->s6_addr[i] = mask; + } + + return addr; +} + +/* Calculate an IPv4 or IPv6 netmask from prefix length, for example /8 -> 255.0.0.0 or /16 -> ffff::. */ +int in_addr_prefixlen_to_netmask(int family, union in_addr_union *addr, unsigned char prefixlen) { + assert(addr); + + switch (family) { + case AF_INET: + in4_addr_prefixlen_to_netmask(&addr->in, prefixlen); + return 0; + case AF_INET6: + in6_addr_prefixlen_to_netmask(&addr->in6, prefixlen); + return 0; + default: + return -EAFNOSUPPORT; + } +} + int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { uint8_t msb_octet = *(uint8_t*) addr; 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 fbc60436..19fa35f1 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -138,6 +138,8 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union 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); +struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen); +int in_addr_prefixlen_to_netmask(int family, union in_addr_union *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); diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c index 8a2b9c2f..fc65a5e7 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.c +++ b/src/libnm-systemd-shared/src/basic/io-util.c @@ -53,11 +53,10 @@ int flush_fd(int fd) { #endif /* NM_IGNORED */ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { - uint8_t *p = buf; + uint8_t *p = ASSERT_PTR(buf); ssize_t n = 0; assert(fd >= 0); - assert(buf); /* If called with nbytes == 0, let's call read() at least * once, to validate the operation */ @@ -113,10 +112,9 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { #if 0 /* NM_IGNORED */ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { - const uint8_t *p = buf; + const uint8_t *p = ASSERT_PTR(buf); assert(fd >= 0); - assert(buf); if (_unlikely_(nbytes > (size_t) SSIZE_MAX)) return -EINVAL; diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 1bc84e46..a6adacf6 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -8,6 +8,7 @@ #include <syslog.h> #include "macro.h" +#include "ratelimit.h" /* Some structures we reference but don't want to pull in headers for */ struct iovec; @@ -508,3 +509,41 @@ int log_syntax_invalid_utf8_internal( #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) void log_setup(void); + +typedef struct LogRateLimit { + int error; + int level; + RateLimit ratelimit; +} LogRateLimit; + +#define log_ratelimit_internal(_level, _error, _format, _file, _line, _func, ...) \ +({ \ + int _log_ratelimit_error = (_error); \ + int _log_ratelimit_level = (_level); \ + static LogRateLimit _log_ratelimit = { \ + .ratelimit = { \ + .interval = 1 * USEC_PER_SEC, \ + .burst = 1, \ + }, \ + }; \ + unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \ + if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \ + ratelimit_reset(&_log_ratelimit.ratelimit); \ + _log_ratelimit.error = _log_ratelimit_error; \ + _log_ratelimit.level = _log_ratelimit_level; \ + } \ + if (ratelimit_below(&_log_ratelimit.ratelimit)) \ + _log_ratelimit_error = _num_dropped_errors > 0 \ + ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", __VA_ARGS__, _num_dropped_errors) \ + : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, __VA_ARGS__); \ + _log_ratelimit_error; \ +}) + +#define log_ratelimit_full_errno(level, error, format, ...) \ + ({ \ + int _level = (level), _e = (error); \ + _e = (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_ratelimit_internal(_level, _e, format, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ + : -ERRNO_VALUE(_e); \ + _e < 0 ? _e : -ESTRPIPE; \ + }) diff --git a/src/libnm-systemd-shared/src/basic/missing_stat.h b/src/libnm-systemd-shared/src/basic/missing_stat.h index 9c1df698..8b39d4f4 100644 --- a/src/libnm-systemd-shared/src/basic/missing_stat.h +++ b/src/libnm-systemd-shared/src/basic/missing_stat.h @@ -49,6 +49,8 @@ struct statx STATX_DEFINITION; /* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc * defines an older definition */ struct new_statx STATX_DEFINITION; +#else /* NM_IGNORED */ +struct new_statx; #endif /* NM_IGNORED */ /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index d5dcaae8..968f01c6 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -482,69 +482,31 @@ int safe_atolli(const char *s, long long int *ret_lli) { return 0; } -int safe_atou8(const char *s, uint8_t *ret) { - unsigned base = 0; - unsigned long l; - char *x = NULL; - - assert(s); - - s += strspn(s, WHITESPACE); - s = mangle_base(s, &base); +int safe_atou8_full(const char *s, unsigned base, uint8_t *ret) { + unsigned u; + int r; - errno = 0; - l = strtoul(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint8_t) l != l) + r = safe_atou_full(s, base, &u); + if (r < 0) + return r; + if (u > UINT8_MAX) return -ERANGE; - if (ret) - *ret = (uint8_t) l; + *ret = (uint8_t) u; return 0; } int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && - strchr(WHITESPACE, s[0])) - return -EINVAL; - - s += strspn(s, WHITESPACE); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && - IN_SET(s[0], '+', '-')) - return -EINVAL; - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && - s[0] == '0' && s[1] != 0) - return -EINVAL; - - s = mangle_base(s, &base); + unsigned u; + int r; - errno = 0; - l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint16_t) l != l) + r = safe_atou_full(s, base, &u); + if (r < 0) + return r; + if (u > UINT16_MAX) return -ERANGE; - if (ret) - *ret = (uint16_t) l; - + *ret = (uint16_t) u; return 0; } diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index f2222dcf..8d8d5232 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -36,7 +36,11 @@ static inline int safe_atou(const char *s, unsigned *ret_u) { int safe_atoi(const char *s, int *ret_i); int safe_atolli(const char *s, long long int *ret_i); -int safe_atou8(const char *s, uint8_t *ret); +int safe_atou8_full(const char *s, unsigned base, uint8_t *ret); + +static inline int safe_atou8(const char *s, uint8_t *ret) { + return safe_atou8_full(s, 0, ret); +} int safe_atou16_full(const char *s, unsigned base, uint16_t *ret); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index 3ba3510f..b45bb0b5 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -3,22 +3,18 @@ #include "nm-sd-adapt-shared.h" #include <errno.h> +#include <fnmatch.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the - * POSIX version which is really broken. We prefer GNU basename(). */ -#include <libgen.h> -#undef basename - #include "alloc-util.h" #include "chase-symlinks.h" #include "extract-word.h" #include "fd-util.h" #include "fs-util.h" +#include "glob-util.h" #include "log.h" #include "macro.h" #include "path-util.h" @@ -200,6 +196,34 @@ int path_make_relative(const char *from, const char *to, char **ret) { return 0; } +int path_make_relative_parent(const char *from_child, const char *to, char **ret) { + _cleanup_free_ char *from = NULL; + int r; + + assert(from_child); + assert(to); + assert(ret); + + /* Similar to path_make_relative(), but provides the relative path from the parent directory of + * 'from_child'. This may be useful when creating relative symlink. + * + * E.g. + * - from = "/path/to/aaa", to = "/path/to/bbb" + * path_make_relative(from, to) = "../bbb" + * path_make_relative_parent(from, to) = "bbb" + * + * - from = "/path/to/aaa/bbb", to = "/path/to/ccc/ddd" + * path_make_relative(from, to) = "../../ccc/ddd" + * path_make_relative_parent(from, to) = "../ccc/ddd" + */ + + r = path_extract_directory(from_child, &from); + if (r < 0) + return r; + + return path_make_relative(from, to, ret); +} + char* path_startswith_strv(const char *p, char **set) { STRV_FOREACH(s, set) { char *t; @@ -328,11 +352,9 @@ char **path_strv_resolve_uniq(char **l, const char *root) { char *path_simplify(char *path) { bool add_slash = false; - char *f = path; + char *f = ASSERT_PTR(path); int r; - assert(path); - /* Removes redundant inner and trailing slashes. Also removes unnecessary dots. * Modifies the passed string in-place. * @@ -765,38 +787,26 @@ static int executable_is_good(const char *executable) { "/dev/null"); } -int fsck_exists(const char *fstype) { +int fsck_exists(void) { + return executable_is_good("fsck"); +} + +int fsck_exists_for_fstype(const char *fstype) { const char *checker; + int r; assert(fstype); if (streq(fstype, "auto")) return -EINVAL; + r = fsck_exists(); + if (r <= 0) + return r; + checker = strjoina("fsck.", fstype); return executable_is_good(checker); } - -char* dirname_malloc(const char *path) { - char *d, *dir, *dir2; - - assert(path); - - d = strdup(path); - if (!d) - return NULL; - - dir = dirname(d); - assert(dir); - - if (dir == d) - return d; - - dir2 = strdup(dir); - free(d); - - return dir2; -} #endif /* NM_IGNORED */ static const char *skip_slash_or_dot(const char *p) { @@ -1319,4 +1329,67 @@ bool prefixed_path_strv_contains(char **l, const char *path) { return false; } + +int path_glob_can_match(const char *pattern, const char *prefix, char **ret) { + assert(pattern); + assert(prefix); + + for (const char *a = pattern, *b = prefix;;) { + _cleanup_free_ char *g = NULL, *h = NULL; + const char *p, *q; + int r, s; + + r = path_find_first_component(&a, /* accept_dot_dot = */ false, &p); + if (r < 0) + return r; + + s = path_find_first_component(&b, /* accept_dot_dot = */ false, &q); + if (s < 0) + return s; + + if (s == 0) { + /* The pattern matches the prefix. */ + if (ret) { + char *t; + + t = path_join(prefix, p); + if (!t) + return -ENOMEM; + + *ret = t; + } + return true; + } + + if (r == 0) + break; + + if (r == s && strneq(p, q, r)) + continue; /* common component. Check next. */ + + g = strndup(p, r); + if (!g) + return -ENOMEM; + + if (!string_is_glob(g)) + break; + + /* We found a glob component. Check if the glob pattern matches the prefix component. */ + + h = strndup(q, s); + if (!h) + return -ENOMEM; + + r = fnmatch(g, h, 0); + if (r == FNM_NOMATCH) + break; + if (r != 0) /* Failure to process pattern? */ + return -EINVAL; + } + + /* The pattern does not match the prefix. */ + if (ret) + *ret = NULL; + return false; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h index c97c4540..949fdc86 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.h +++ b/src/libnm-systemd-shared/src/basic/path-util.h @@ -63,6 +63,7 @@ char* path_make_absolute(const char *p, const char *prefix); int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from, const char *to, char **ret); +int path_make_relative_parent(const char *from_child, const char *to, char **ret); char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_; static inline char* path_startswith(const char *path, const char *prefix) { return path_startswith_full(path, prefix, true); @@ -104,7 +105,8 @@ static inline int find_executable(const char *name, char **ret_filename) { bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); -int fsck_exists(const char *fstype); +int fsck_exists(void); +int fsck_exists_for_fstype(const char *fstype); /* Iterates through the path prefixes of the specified path, going up * the tree, to root. Also returns "" (and not "/"!) for the root @@ -153,7 +155,6 @@ int fsck_exists(const char *fstype); _ret; \ }) -char* dirname_malloc(const char *path); int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret); int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret); const char *last_path_component(const char *path); @@ -198,3 +199,5 @@ static inline const char *empty_to_root(const char *path) { bool path_strv_contains(char **l, const char *path); bool prefixed_path_strv_contains(char **l, const char *path); + +int path_glob_can_match(const char *pattern, const char *prefix, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 1c23a2f8..428323ef 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -821,7 +821,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { if (status.si_pid == pid) { /* This is the correct child. */ if (status.si_code == CLD_EXITED) - return (status.si_status == 0) ? 0 : -EPROTO; + return status.si_status == 0 ? 0 : -EPROTO; else return -EPROTO; } diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index d576bff1..9e1a746f 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -2,10 +2,6 @@ #include "nm-sd-adapt-shared.h" -#if defined(__i386__) || defined(__x86_64__) -#include <cpuid.h> -#endif - #include <elf.h> #include <errno.h> #include <fcntl.h> @@ -35,11 +31,10 @@ #include "sha256.h" #include "time-util.h" -/* This is a "best effort" kind of thing, but has no real security value. - * So, this should only be used by random_bytes(), which is not meant for - * crypto. This could be made better, but we're *not* trying to roll a - * userspace prng here, or even have forward secrecy, but rather just do - * the shortest thing that is at least better than libc rand(). */ +/* This is a "best effort" kind of thing, but has no real security value. So, this should only be used by + * random_bytes(), which is not meant for crypto. This could be made better, but we're *not* trying to roll a + * userspace prng here, or even have forward secrecy, but rather just do the shortest thing that is at least + * better than libc rand(). */ static void fallback_random_bytes(void *p, size_t n) { static thread_local uint64_t fallback_counter = 0; struct { @@ -55,7 +50,7 @@ static void fallback_random_bytes(void *p, size_t n) { .stamp_mono = now(CLOCK_MONOTONIC), .stamp_real = now(CLOCK_REALTIME), .pid = getpid(), - .tid = gettid() + .tid = gettid(), }; #if HAVE_SYS_AUXV_H diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.c b/src/libnm-systemd-shared/src/basic/ratelimit.c index fba05875..3ccec119 100644 --- a/src/libnm-systemd-shared/src/basic/ratelimit.c +++ b/src/libnm-systemd-shared/src/basic/ratelimit.c @@ -32,9 +32,16 @@ bool ratelimit_below(RateLimit *r) { if (r->num < r->burst) goto good; + r->num++; return false; good: r->num++; return true; } + +unsigned ratelimit_num_dropped(RateLimit *r) { + assert(r); + + return r->num > r->burst ? r->num - r->burst : 0; +} diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.h b/src/libnm-systemd-shared/src/basic/ratelimit.h index ee1d17c0..22361898 100644 --- a/src/libnm-systemd-shared/src/basic/ratelimit.h +++ b/src/libnm-systemd-shared/src/basic/ratelimit.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include "time-util.h" -#include "util.h" typedef struct RateLimit { usec_t interval; /* Keep those two fields first so they can be initialized easily: */ @@ -22,3 +21,5 @@ static inline bool ratelimit_configured(RateLimit *rl) { } bool ratelimit_below(RateLimit *r); + +unsigned ratelimit_num_dropped(RateLimit *r); diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index 8c5858ff..5a142263 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -373,6 +373,32 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ } +bool statx_inode_same(const struct statx *a, const struct statx *b) { + + /* Same as stat_inode_same() but for struct statx */ + + return a && b && + FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) && + (a->stx_mode & S_IFMT) != 0 && + ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 && + a->stx_dev_major == b->stx_dev_major && + a->stx_dev_minor == b->stx_dev_minor && + a->stx_ino == b->stx_ino; +} + +bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) { + if (!a || !b) + return false; + + /* if we have the mount ID, that's all we need */ + if (FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID)) + return a->stx_mnt_id == b->stx_mnt_id; + + /* Otherwise, major/minor of backing device must match */ + return a->stx_dev_major == b->stx_dev_major && + a->stx_dev_minor == b->stx_dev_minor; +} + int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) { static bool avoid_statx = false; struct stat st; diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h index 56f15534..f9519d8c 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.h +++ b/src/libnm-systemd-shared/src/basic/stat-util.h @@ -73,6 +73,9 @@ int proc_mounted(void); bool stat_inode_same(const struct stat *a, const struct stat *b); bool stat_inode_unmodified(const struct stat *a, const struct stat *b); +bool statx_inode_same(const struct statx *a, const struct statx *b); +bool statx_mount_same(const struct new_statx *a, const struct new_statx *b); + int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); #if HAS_FEATURE_MEMORY_SANITIZER diff --git a/src/libnm-systemd-shared/src/basic/stdio-util.h b/src/libnm-systemd-shared/src/basic/stdio-util.h index f3b213c5..07cbfe9d 100644 --- a/src/libnm-systemd-shared/src/basic/stdio-util.h +++ b/src/libnm-systemd-shared/src/basic/stdio-util.h @@ -11,16 +11,16 @@ #include "macro.h" #include "memory-util.h" -#define snprintf_ok(buf, len, fmt, ...) \ - ({ \ - char *_buf = (buf); \ - size_t _len = (len); \ - int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__); \ - _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \ +#define snprintf_ok(buf, len, fmt, ...) \ + ({ \ + char *_buf = (buf); \ + size_t _len = (len); \ + int _snpf = snprintf(_buf, _len, (fmt), ##__VA_ARGS__); \ + _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \ }) #define xsprintf(buf, fmt, ...) \ - assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") + assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: " #buf "[] must be big enough") #define VA_FORMAT_ADVANCE(format, ap) \ do { \ diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index 7cd4b055..9b2ee879 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -516,7 +516,6 @@ char *cellescape(char *buf, size_t len, const char *s) { buf[i] = '\0'; return buf; } -#endif /* NM_IGNORED */ char* strshorten(char *s, size_t l) { assert(s); @@ -527,6 +526,20 @@ char* strshorten(char *s, size_t l) { return s; } +int strgrowpad0(char **s, size_t l) { + assert(s); + + char *q = realloc(*s, l); + if (!q) + return -ENOMEM; + *s = q; + + size_t sz = strlen(*s); + memzero(*s + sz, l - sz); + return 0; +} +#endif /* NM_IGNORED */ + char *strreplace(const char *text, const char *old_string, const char *new_string) { size_t l, old_len, new_len; char *t, *ret = NULL; diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index 1dd46f7f..46681ced 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -152,6 +152,8 @@ char *cellescape(char *buf, size_t len, const char *s); char* strshorten(char *s, size_t l); +int strgrowpad0(char **s, size_t l); + char *strreplace(const char *text, const char *old_string, const char *new_string); char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); @@ -169,9 +171,12 @@ int split_pair(const char *s, const char *sep, char **l, char **r); int free_and_strdup(char **p, const char *s); static inline int free_and_strdup_warn(char **p, const char *s) { - if (free_and_strdup(p, s) < 0) + int r; + + r = free_and_strdup(p, s); + if (r < 0) return log_oom(); - return 0; + return r; } int free_and_strndup(char **p, const char *s, size_t l); diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index d9c58633..48ca8575 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -840,13 +840,26 @@ char** strv_shell_escape(char **l, const char *bad) { } #endif /* NM_IGNORED */ -bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) { - for (size_t i = 0; patterns && patterns[i]; i++) - if (fnmatch(patterns[i], s, flags) == 0) { - if (matched_pos) - *matched_pos = i; - return true; - } +bool strv_fnmatch_full( + char* const* patterns, + const char *s, + int flags, + size_t *ret_matched_pos) { + + assert(s); + + if (patterns) + for (size_t i = 0; patterns[i]; i++) + /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to + * process the pattern for some reason we'll consider this equivalent to non-matching. */ + if (fnmatch(patterns[i], s, flags) == 0) { + if (ret_matched_pos) + *ret_matched_pos = i; + return true; + } + + if (ret_matched_pos) + *ret_matched_pos = SIZE_MAX; return false; } diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index 87ec6337..d6f5ac6b 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -240,7 +240,7 @@ void strv_print(char * const *l); 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); +bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *ret_matched_pos); static inline bool strv_fnmatch(char* const* patterns, const char *s) { return strv_fnmatch_full(patterns, s, 0, NULL); } diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index 0c8f4929..cf556a73 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -517,10 +517,9 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { { "us", 1 }, }; - char *p = buf; + char *p = ASSERT_PTR(buf); bool something = false; - assert(buf); assert(l > 0); if (t == USEC_INFINITY) { diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h index bf312442..c98f95a5 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.h +++ b/src/libnm-systemd-shared/src/basic/time-util.h @@ -189,10 +189,15 @@ static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { } static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { + if (delta == INT64_MIN) { /* prevent overflow */ + assert_cc(-(INT64_MIN + 1) == INT64_MAX); + assert_cc(USEC_INFINITY > INT64_MAX); + return usec_add(timestamp, (usec_t) INT64_MAX + 1); + } if (delta < 0) return usec_add(timestamp, (usec_t) (-delta)); - else - return usec_sub_unsigned(timestamp, (usec_t) delta); + + return usec_sub_unsigned(timestamp, (usec_t) delta); } #if SIZEOF_TIME_T == 8 diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index 57447a42..2a27f86b 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -90,51 +90,92 @@ int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { } #endif /* NM_IGNORED */ -int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL; +static int tempfn_build(const char *p, const char *pre, const char *post, bool child, char **ret) { + _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL, *result = NULL; + size_t len_pre, len_post, len_add; int r; + assert(p); assert(ret); /* * Turns this: * /foo/bar/waldo * - * Into this: - * /foo/bar/.#<extra>waldoXXXXXX + * Into this : + * /foo/bar/waldo/.#<pre><post> (child == true) + * /foo/bar/.#<pre>waldo<post> (child == false) */ - r = path_extract_directory(p, &d); - if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */ - return r; + if (pre && strchr(pre, '/')) + return -EINVAL; - r = path_extract_filename(p, &fn); - if (r < 0) - return r; + if (post && strchr(post, '/')) + return -EINVAL; + + len_pre = strlen_ptr(pre); + len_post = strlen_ptr(post); + /* NAME_MAX is counted *without* the trailing NUL byte. */ + if (len_pre > NAME_MAX - STRLEN(".#") || + len_post > NAME_MAX - STRLEN(".#") - len_pre) + return -EINVAL; + + len_add = len_pre + len_post + STRLEN(".#"); + + if (child) { + d = strdup(p); + if (!d) + return -ENOMEM; + } else { + r = path_extract_directory(p, &d); + if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */ + return r; - nf = strjoin(".#", strempty(extra), fn, "XXXXXX"); + r = path_extract_filename(p, &fn); + if (r < 0) + return r; + + if (strlen(fn) > NAME_MAX - len_add) + /* We cannot simply prepend and append strings to the filename. Let's truncate the filename. */ + fn[NAME_MAX - len_add] = '\0'; + } + + nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post)); if (!nf) return -ENOMEM; - if (!filename_is_valid(nf)) /* New name is not valid? (Maybe because too long?) Refuse. */ - return -EINVAL; - - if (d) { + if (d) { if (!path_extend(&d, nf)) return -ENOMEM; - *ret = path_simplify(TAKE_PTR(d)); + result = path_simplify(TAKE_PTR(d)); } else - *ret = TAKE_PTR(nf); + result = TAKE_PTR(nf); + + if (!path_is_valid(result)) /* New path is not valid? (Maybe because too long?) Refuse. */ + return -EINVAL; + *ret = TAKE_PTR(result); return 0; } +int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#<extra>waldoXXXXXX + */ + + return tempfn_build(p, extra, "XXXXXX", /* child = */ false, ret); +} + #if 0 /* NM_IGNORED */ int tempfn_random(const char *p, const char *extra, char **ret) { - _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL; - int r; + _cleanup_free_ char *s = NULL; + assert(p); assert(ret); /* @@ -145,37 +186,14 @@ int tempfn_random(const char *p, const char *extra, char **ret) { * /foo/bar/.#<extra>waldobaa2a261115984a9 */ - r = path_extract_directory(p, &d); - if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */ - return r; - - r = path_extract_filename(p, &fn); - if (r < 0) - return r; - - if (asprintf(&nf, ".#%s%s%016" PRIx64, - strempty(extra), - fn, - random_u64()) < 0) + if (asprintf(&s, "%016" PRIx64, random_u64()) < 0) return -ENOMEM; - if (!filename_is_valid(nf)) /* Not valid? (maybe because too long now?) — refuse early */ - return -EINVAL; - - if (d) { - if (!path_extend(&d, nf)) - return -ENOMEM; - - *ret = path_simplify(TAKE_PTR(d)); - } else - *ret = TAKE_PTR(nf); - - return 0; + return tempfn_build(p, extra, s, /* child = */ false, ret); } int tempfn_random_child(const char *p, const char *extra, char **ret) { - char *t, *x; - uint64_t u; + _cleanup_free_ char *s = NULL; int r; assert(ret); @@ -192,27 +210,10 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { return r; } - extra = strempty(extra); - - t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); - if (!t) + if (asprintf(&s, "%016" PRIx64, random_u64()) < 0) return -ENOMEM; - if (isempty(p)) - x = stpcpy(stpcpy(t, ".#"), extra); - else - x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); - - u = random_u64(); - for (unsigned i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t); - return 0; + return tempfn_build(p, extra, s, /* child = */ true, ret); } int open_tmpfile_unlinkable(const char *directory, int flags) { diff --git a/src/libnm-systemd-shared/src/basic/user-util.h b/src/libnm-systemd-shared/src/basic/user-util.h new file mode 100644 index 00000000..a08683bc --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/user-util.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <grp.h> +#if ENABLE_GSHADOW +# include <gshadow.h> +#endif +#include <pwd.h> +#include <shadow.h> +#include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> + +/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */ +#define HOME_UID_MIN ((uid_t) 60001) +#define HOME_UID_MAX ((uid_t) 60513) + +/* Users mapped from host into a container */ +#define MAP_UID_MIN ((uid_t) 60514) +#define MAP_UID_MAX ((uid_t) 60577) + +bool uid_is_valid(uid_t uid); + +static inline bool gid_is_valid(gid_t gid) { + return uid_is_valid((uid_t) gid); +} + +int parse_uid(const char *s, uid_t* ret_uid); +int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper); + +static inline int parse_gid(const char *s, gid_t *ret_gid) { + return parse_uid(s, (uid_t*) ret_gid); +} + +char* getlogname_malloc(void); +char* getusername_malloc(void); + +typedef enum UserCredsFlags { + USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */ + USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */ + USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */ +} UserCredsFlags; + +int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags); +int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags); + +char* uid_to_name(uid_t uid); +char* gid_to_name(gid_t gid); + +int in_gid(gid_t gid); +int in_group(const char *name); + +int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **result); +int getgroups_alloc(gid_t** gids); + +int get_home_dir(char **ret); +int get_shell(char **ret); + +int reset_uid_gid(void); + +int take_etc_passwd_lock(const char *root); + +#define UID_INVALID ((uid_t) -1) +#define GID_INVALID ((gid_t) -1) + +#define UID_NOBODY ((uid_t) 65534U) +#define GID_NOBODY ((gid_t) 65534U) + +/* If REMOUNT_IDMAPPING_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host + * root user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in + * the *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we + * definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range + * we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use + * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is + * icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following + * our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID + * value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read + * this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */ +#define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1)) +#define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1)) + +#define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock" + +/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer + * NULL is special */ +#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1)) +#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) + +#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1)) +#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) + +static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; +} + +typedef enum ValidUserFlags { + VALID_USER_RELAX = 1 << 0, + VALID_USER_WARN = 1 << 1, + VALID_USER_ALLOW_NUMERIC = 1 << 2, +} ValidUserFlags; + +bool valid_user_group_name(const char *u, ValidUserFlags flags); +bool valid_gecos(const char *d); +char *mangle_gecos(const char *d); +bool valid_home(const char *p); + +static inline bool valid_shell(const char *p) { + /* We have the same requirements, so just piggy-back on the home check. + * + * Let's ignore /etc/shells because this is only applicable to real and + * not system users. It is also incompatible with the idea of empty /etc. + */ + return valid_home(p); +} + +int maybe_setgroups(size_t size, const gid_t *list); + +bool synthesize_nobody(void); + +int fgetpwent_sane(FILE *stream, struct passwd **pw); +int fgetspent_sane(FILE *stream, struct spwd **sp); +int fgetgrent_sane(FILE *stream, struct group **gr); +int putpwent_sane(const struct passwd *pw, FILE *stream); +int putspent_sane(const struct spwd *sp, FILE *stream); +int putgrent_sane(const struct group *gr, FILE *stream); +#if ENABLE_GSHADOW +int fgetsgent_sane(FILE *stream, struct sgrp **sg); +int putsgent_sane(const struct sgrp *sg, FILE *stream); +#endif + +bool is_nologin_shell(const char *shell); +const char* default_root_shell(const char *root); + +int is_this_me(const char *username); + +const char *get_home_root(void); + +static inline bool hashed_password_is_locked_or_invalid(const char *password) { + return password && password[0] != '$'; +} + +/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */ +#define PASSWORD_LOCKED_AND_INVALID "!*" + +/* A password indicating "look in shadow file, please!" for "struct passwd"'s .pw_passwd */ +#define PASSWORD_SEE_SHADOW "x" + +/* A password indicating "hey, no password required for login" */ +#define PASSWORD_NONE "" diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index 0ed99403..0c69d98a 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -81,18 +81,13 @@ #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_; \ - }) - -#define ASSERT_SE_PTR(expr) \ - ({ \ - typeof(expr) _expr_ = (expr); \ - assert_se(_expr_); \ - _expr_; \ +#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert) +#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se) +#define _ASSERT_PTR(expr, var, check) \ + ({ \ + typeof(expr) var = (expr); \ + check(var); \ + var; \ }) #define ASSERT_NONNEG(expr) \ diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c index 0f3872ae..b4c36ab3 100644 --- a/src/libnm-systemd-shared/src/fundamental/sha256.c +++ b/src/libnm-systemd-shared/src/fundamental/sha256.c @@ -106,7 +106,7 @@ void sha256_init_ctx(struct sha256_ctx *ctx) { /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. */ -void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) { +uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]) { /* Take yet unprocessed bytes into account. */ uint32_t bytes = ctx->buflen; size_t pad; @@ -131,9 +131,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) { /* Put result from CTX in first 32 bytes following RESBUF. */ for (size_t i = 0; i < 8; ++i) if (UNALIGNED_P(resbuf)) - memcpy((uint8_t*) resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t)); + memcpy(resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t)); else - ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]); + ((uint32_t *) (void *) resbuf)[i] = SWAP(ctx->H[i]); return resbuf; } @@ -199,10 +199,9 @@ void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. */ static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) { - const uint32_t *words = buffer; + const uint32_t *words = ASSERT_PTR(buffer); size_t nwords = len / sizeof(uint32_t); - assert(buffer); assert(ctx); uint32_t a = ctx->H[0]; @@ -291,3 +290,10 @@ static void sha256_process_block(const void *buffer, size_t len, struct sha256_c ctx->H[6] = g; ctx->H[7] = h; } + +uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]) { + struct sha256_ctx ctx; + sha256_init_ctx(&ctx); + sha256_process_bytes(buffer, sz, &ctx); + return sha256_finish_ctx(&ctx, result); +} diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.h b/src/libnm-systemd-shared/src/fundamental/sha256.h index f296f76a..31790c2e 100644 --- a/src/libnm-systemd-shared/src/fundamental/sha256.h +++ b/src/libnm-systemd-shared/src/fundamental/sha256.h @@ -25,5 +25,9 @@ struct sha256_ctx { }; void sha256_init_ctx(struct sha256_ctx *ctx); -void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf); +uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]); void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx); + +uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]); + +#define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {}) |