summary refs log tree commit diff
path: root/src/libnm-systemd-shared
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
committerMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
commit1372848511cb896b80b51ed1a3e9606bd9816631 (patch)
tree674792b9385bdef935988894b45f06b2af39f88c /src/libnm-systemd-shared
parent40ec077ea305994c1fc2130add6787ca0c73e2c6 (diff)
New upstream version 1.42.0 upstream/1.42.0
Diffstat (limited to 'src/libnm-systemd-shared')
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/user-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/cgroup-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.c4
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c12
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/errno-util.h31
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c19
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c77
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h22
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c9
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c53
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h39
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_stat.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c68
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c135
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c15
-rw-r--r--src/libnm-systemd-shared/src/basic/ratelimit.c7
-rw-r--r--src/libnm-systemd-shared/src/basic/ratelimit.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c26
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/stdio-util.h14
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.c15
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c3
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/tmpfile-util.c129
-rw-r--r--src/libnm-systemd-shared/src/basic/user-util.h150
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h19
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256.c16
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256.h6
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]) {})