summary refs log tree commit diff
path: root/src/libnm-systemd-shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared')
-rw-r--r--src/libnm-systemd-shared/meson.build1
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/chase-symlinks.h3
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/missing_ioprio.h3
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h116
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/sync-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h24
-rw-r--r--src/libnm-systemd-shared/src/basic/cgroup-util.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c84
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.c53
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h36
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.c56
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c509
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h27
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c211
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.h41
-rw-r--r--src/libnm-systemd-shared/src/basic/format-util.c62
-rw-r--r--src/libnm-systemd-shared/src/basic/format-util.h36
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c805
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h45
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c11
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.c58
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/hostname-util.c71
-rw-r--r--src/libnm-systemd-shared/src/basic/hostname-util.h38
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c37
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.c31
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.h22
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h39
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h59
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.c13
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.h14
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_syscall.h20
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c11
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c110
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/prioq.c10
-rw-r--r--src/libnm-systemd-shared/src/basic/prioq.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c155
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/set.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.c37
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c58
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h34
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c5
-rw-r--r--src/libnm-systemd-shared/src/basic/stdio-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h18
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c20
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c281
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.h46
-rw-r--r--src/libnm-systemd-shared/src/basic/tmpfile-util.c6
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h54
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c12
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h17
-rw-r--r--src/libnm-systemd-shared/src/shared/log-link.h18
-rw-r--r--src/libnm-systemd-shared/src/shared/web-util.c17
-rw-r--r--src/libnm-systemd-shared/src/shared/web-util.h1
68 files changed, 1506 insertions, 2092 deletions
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build
index 68daa20e..05c81faf 100644
--- a/src/libnm-systemd-shared/meson.build
+++ b/src/libnm-systemd-shared/meson.build
@@ -19,6 +19,7 @@ libnm_systemd_shared = static_library(
     'src/basic/hexdecoct.c',
     'src/basic/hostname-util.c',
     'src/basic/in-addr-util.c',
+    'src/basic/inotify-util.c',
     'src/basic/io-util.c',
     'src/basic/memory-util.c',
     'src/basic/mempool.c',
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/chase-symlinks.h b/src/libnm-systemd-shared/sd-adapt-shared/chase-symlinks.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/libnm-systemd-shared/sd-adapt-shared/chase-symlinks.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/missing_ioprio.h b/src/libnm-systemd-shared/sd-adapt-shared/missing_ioprio.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/libnm-systemd-shared/sd-adapt-shared/missing_ioprio.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h
index 2f6188dc..586c3a7e 100644
--- a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h
+++ b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h
@@ -29,7 +29,7 @@
 /*****************************************************************************/
 
 #ifndef VALGRIND
-    #define VALGRIND 0
+#define VALGRIND 0
 #endif
 
 #define ENABLE_DEBUG_HASHMAP 0
@@ -41,17 +41,17 @@
 
 #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD
 
-    #include <sys/syscall.h>
-    #include <sys/ioctl.h>
-    #include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
 
-    #define ENABLE_GSHADOW FALSE
+#define ENABLE_GSHADOW FALSE
 
-    #define HAVE_SECCOMP 0
+#define HAVE_SECCOMP 0
 
-    #define LOG_TRACE 0
+#define LOG_TRACE 0
 
-    #define BUILD_MODE_DEVELOPER (NM_MORE_ASSERTS > 0)
+#define BUILD_MODE_DEVELOPER (NM_MORE_ASSERTS > 0)
 
 /*****************************************************************************/
 
@@ -68,63 +68,63 @@ struct statx;
 static inline pid_t
 raw_getpid(void)
 {
-    #if defined(__alpha__)
+#if defined(__alpha__)
     return (pid_t) syscall(__NR_getxpid);
-    #else
+#else
     return (pid_t) syscall(__NR_getpid);
-    #endif
+#endif
 }
 
-    #define gettid() nm_utils_gettid()
-
-    /* we build with C11 and thus <uchar.h> provides char32_t,char16_t. */
-    #define HAVE_CHAR32_T 1
-    #define HAVE_CHAR16_T 1
-
-    #if defined(HAVE_DECL_REALLOCARRAY) && HAVE_DECL_REALLOCARRAY == 1
-        #define HAVE_REALLOCARRAY 1
-    #else
-        #define HAVE_REALLOCARRAY 0
-    #endif
-
-    #if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1
-        #define HAVE_EXPLICIT_BZERO 1
-    #else
-        #define HAVE_EXPLICIT_BZERO 0
-    #endif
-
-    #if defined(HAVE_DECL_PIDFD_OPEN) && HAVE_DECL_PIDFD_OPEN == 1
-        #define HAVE_PIDFD_OPEN 1
-    #else
-        #define HAVE_PIDFD_OPEN 0
-    #endif
-
-    #if defined(HAVE_DECL_PIDFD_SEND_SIGNAL) && HAVE_DECL_PIDFD_SEND_SIGNAL == 1
-        #define HAVE_PIDFD_SEND_SIGNAL 1
-    #else
-        #define HAVE_PIDFD_SEND_SIGNAL 0
-    #endif
-
-    #if defined(HAVE_DECL_RT_SIGQUEUEINFO) && HAVE_DECL_RT_SIGQUEUEINFO == 1
-        #define HAVE_RT_SIGQUEUEINFO 1
-    #else
-        #define HAVE_RT_SIGQUEUEINFO 0
-    #endif
-
-    #ifndef ALTIFNAMSIZ
-        #define ALTIFNAMSIZ 128
-    #endif
-
-    #define HAVE_LINUX_TIME_TYPES_H 0
-
-    #ifndef __COMPAR_FN_T
-        #define __COMPAR_FN_T
+#define gettid() nm_utils_gettid()
+
+/* we build with C11 and thus <uchar.h> provides char32_t,char16_t. */
+#define HAVE_CHAR32_T 1
+#define HAVE_CHAR16_T 1
+
+#if defined(HAVE_DECL_REALLOCARRAY) && HAVE_DECL_REALLOCARRAY == 1
+#define HAVE_REALLOCARRAY 1
+#else
+#define HAVE_REALLOCARRAY 0
+#endif
+
+#if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1
+#define HAVE_EXPLICIT_BZERO 1
+#else
+#define HAVE_EXPLICIT_BZERO 0
+#endif
+
+#if defined(HAVE_DECL_PIDFD_OPEN) && HAVE_DECL_PIDFD_OPEN == 1
+#define HAVE_PIDFD_OPEN 1
+#else
+#define HAVE_PIDFD_OPEN 0
+#endif
+
+#if defined(HAVE_DECL_PIDFD_SEND_SIGNAL) && HAVE_DECL_PIDFD_SEND_SIGNAL == 1
+#define HAVE_PIDFD_SEND_SIGNAL 1
+#else
+#define HAVE_PIDFD_SEND_SIGNAL 0
+#endif
+
+#if defined(HAVE_DECL_RT_SIGQUEUEINFO) && HAVE_DECL_RT_SIGQUEUEINFO == 1
+#define HAVE_RT_SIGQUEUEINFO 1
+#else
+#define HAVE_RT_SIGQUEUEINFO 0
+#endif
+
+#ifndef ALTIFNAMSIZ
+#define ALTIFNAMSIZ 128
+#endif
+
+#define HAVE_LINUX_TIME_TYPES_H 0
+
+#ifndef __COMPAR_FN_T
+#define __COMPAR_FN_T
 typedef int (*__compar_fn_t)(const void *, const void *);
 typedef __compar_fn_t comparison_fn_t;
 typedef int (*__compar_d_fn_t)(const void *, const void *, void *);
-    #endif
+#endif
 
-    #ifndef __GLIBC__
+#ifndef __GLIBC__
 static inline int
 __register_atfork(void (*prepare)(void),
                   void (*parent)(void),
@@ -133,7 +133,7 @@ __register_atfork(void (*prepare)(void),
 {
     return pthread_atfork(prepare, parent, child);
 }
-    #endif
+#endif
 
 #endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */
 
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/sync-util.h b/src/libnm-systemd-shared/sd-adapt-shared/sync-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/libnm-systemd-shared/sd-adapt-shared/sync-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index 99fbd3a8..e587fe79 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -44,16 +44,19 @@ typedef void (*free_func_t)(void *p);
 
 #define malloc0(n) (calloc(1, (n) ?: 1))
 
-static inline void *mfree(void *memory) {
-        free(memory);
-        return NULL;
-}
+#define mfree(memory)                           \
+        ({                                      \
+                free(memory);                   \
+                (typeof(memory)) NULL;          \
+        })
 
 #define free_and_replace(a, b)                  \
         ({                                      \
-                free(a);                        \
-                (a) = (b);                      \
-                (b) = NULL;                     \
+                typeof(a)* _a = &(a);           \
+                typeof(b)* _b = &(b);           \
+                free(*_a);                      \
+                (*_a) = (*_b);                  \
+                (*_b) = NULL;                   \
                 0;                              \
         })
 
@@ -79,6 +82,13 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
                 memcpy_safe(_q_, p, _l_);       \
         })
 
+static inline void unsetp(void *p) {
+        /* A trivial "destructor" that can be used in cases where we want to
+         * unset a pointer from a _cleanup_ function. */
+
+        *(void**)p = NULL;
+}
+
 static inline void freep(void *p) {
         *(void**)p = mfree(*(void**) p);
 }
diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h
index ce2f4c65..90ccd2c0 100644
--- a/src/libnm-systemd-shared/src/basic/cgroup-util.h
+++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h
@@ -32,6 +32,7 @@ typedef enum CGroupController {
         CGROUP_CONTROLLER_BPF_DEVICES,
         CGROUP_CONTROLLER_BPF_FOREIGN,
         CGROUP_CONTROLLER_BPF_SOCKET_BIND,
+        CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
 
         _CGROUP_CONTROLLER_MAX,
         _CGROUP_CONTROLLER_INVALID = -EINVAL,
@@ -53,6 +54,7 @@ typedef enum CGroupMask {
         CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
         CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
         CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND),
+        CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES),
 
         /* All real cgroup v1 controllers */
         CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
@@ -61,7 +63,7 @@ typedef enum CGroupMask {
         CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
 
         /* All cgroup v2 BPF pseudo-controllers */
-        CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
+        CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
 
         _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
 } CGroupMask;
@@ -172,6 +174,7 @@ typedef enum CGroupFlags {
 typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
 
 int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill_kernel_sigkill(const char *controller, const char *path);
 int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
 
 int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
@@ -272,6 +275,7 @@ int cg_kernel_controllers(Set **controllers);
 
 bool cg_ns_supported(void);
 bool cg_freezer_supported(void);
+bool cg_kill_supported(void);
 
 int cg_all_unified(void);
 int cg_hybrid_unified(void);
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index 849ee47e..431862f6 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -186,39 +186,51 @@ static int env_append(char **r, char ***k, char **a) {
         return 0;
 }
 
-char **strv_env_merge(size_t n_lists, ...) {
-        _cleanup_strv_free_ char **ret = NULL;
-        size_t n = 0;
-        char **l, **k;
+char** _strv_env_merge(char **first, ...) {
+        _cleanup_strv_free_ char **merged = NULL;
+        char **k;
         va_list ap;
 
         /* Merges an arbitrary number of environment sets */
 
-        va_start(ap, n_lists);
-        for (size_t i = 0; i < n_lists; i++) {
+        size_t n = strv_length(first);
+
+        va_start(ap, first);
+        for (;;) {
+                char **l;
+
                 l = va_arg(ap, char**);
+                if (l == POINTER_MAX)
+                        break;
+
                 n += strv_length(l);
         }
         va_end(ap);
 
-        ret = new(char*, n+1);
-        if (!ret)
+        k = merged = new(char*, n + 1);
+        if (!merged)
+                return NULL;
+        merged[0] = NULL;
+
+        if (env_append(merged, &k, first) < 0)
                 return NULL;
 
-        *ret = NULL;
-        k = ret;
+        va_start(ap, first);
+        for (;;) {
+                char **l;
 
-        va_start(ap, n_lists);
-        for (size_t i = 0; i < n_lists; i++) {
                 l = va_arg(ap, char**);
-                if (env_append(ret, &k, l) < 0) {
+                if (l == POINTER_MAX)
+                        break;
+
+                if (env_append(merged, &k, l) < 0) {
                         va_end(ap);
                         return NULL;
                 }
         }
         va_end(ap);
 
-        return TAKE_PTR(ret);
+        return TAKE_PTR(merged);
 }
 
 static bool env_match(const char *t, const char *pattern) {
@@ -412,6 +424,32 @@ int strv_env_replace_strdup(char ***l, const char *assignment) {
         return strv_env_replace_consume(l, p);
 }
 
+int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
+        /* Like strv_env_replace_strdup(), but pulls the variable from the environment of
+         * the calling program, if a variable name without value is specified.
+         */
+        char *p;
+
+        if (strchr(assignment, '=')) {
+                if (!env_assignment_is_valid(assignment))
+                        return -EINVAL;
+
+                p = strdup(assignment);
+        } else {
+                if (!env_name_is_valid(assignment))
+                        return -EINVAL;
+
+                /* If we can't find the variable in our environment, we will use
+                 * the empty string. This way "passthrough" is equivalent to passing
+                 * --setenv=FOO=$FOO in the shell. */
+                p = strjoin(assignment, "=", secure_getenv(assignment));
+        }
+        if (!p)
+                return -ENOMEM;
+
+        return strv_env_replace_consume(l, p);
+}
+
 int strv_env_assign(char ***l, const char *key, const char *value) {
         if (!env_name_is_valid(key))
                 return -EINVAL;
@@ -580,6 +618,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
 
                                 word = e+1;
                                 state = WORD;
+                                nest--;
                         } else if (*e == ':') {
                                 if (flags & REPLACE_ENV_ALLOW_EXTENDED) {
                                         len = e - word - 2;
@@ -837,4 +876,21 @@ int getenv_path_list(const char *name, char ***ret_paths) {
         *ret_paths = TAKE_PTR(l);
         return 1;
 }
+
+int unsetenv_erase(const char *name) {
+        char *p;
+
+        assert(name);
+
+        p = getenv(name);
+        if (!p)
+                return 0;
+
+        string_erase(p);
+
+        if (unsetenv(name) < 0)
+                return -errno;
+
+        return 1;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h
index 1fbe7e42..38bfc8a3 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.h
+++ b/src/libnm-systemd-shared/src/basic/env-util.h
@@ -39,13 +39,15 @@ char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const cha
 bool strv_env_name_is_valid(char **l);
 bool strv_env_name_or_assignment_is_valid(char **l);
 
-char **strv_env_merge(size_t n_lists, ...);
+char** _strv_env_merge(char **first, ...);
+#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
 char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
 
 char **strv_env_unset(char **l, const char *p); /* In place ... */
 char **strv_env_unset_many(char **l, ...) _sentinel_;
 int strv_env_replace_consume(char ***l, char *p); /* In place ... */
 int strv_env_replace_strdup(char ***l, const char *assignment);
+int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
 int strv_env_assign(char ***l, const char *key, const char *value);
 
 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
@@ -66,3 +68,5 @@ int setenv_systemd_exec_pid(bool update_only);
 /* Parses and does sanity checks on an environment variable containing
  * PATH-like colon-separated absolute paths */
 int getenv_path_list(const char *name, char ***ret_paths);
+
+int unsetenv_erase(const char *name);
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index 6a138020..93288563 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -10,6 +10,7 @@
 #include "escape.h"
 #include "hexdecoct.h"
 #include "macro.h"
+#include "strv.h"
 #include "utf8.h"
 
 int cescape_char(char c, char *buf) {
@@ -291,10 +292,12 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
 }
 
 #if 0 /* NM_IGNORED */
-int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
-        char *r, *t;
+ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
+        _cleanup_free_ char *ans = NULL;
+        char *t;
         const char *f;
         size_t pl;
+        int r;
 
         assert(s);
         assert(ret);
@@ -303,18 +306,17 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
 
         pl = strlen_ptr(prefix);
 
-        r = new(char, pl+length+1);
-        if (!r)
+        ans = new(char, pl+length+1);
+        if (!ans)
                 return -ENOMEM;
 
         if (prefix)
-                memcpy(r, prefix, pl);
+                memcpy(ans, prefix, pl);
 
-        for (f = s, t = r + pl; f < s + length; f++) {
+        for (f = s, t = ans + pl; f < s + length; f++) {
                 size_t remaining;
                 bool eight_bit = false;
                 char32_t u;
-                int k;
 
                 remaining = s + length - f;
                 assert(remaining > 0);
@@ -332,23 +334,21 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
                                 continue;
                         }
 
-                        free(r);
                         return -EINVAL;
                 }
 
-                k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
-                if (k < 0) {
+                r = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
+                if (r < 0) {
                         if (flags & UNESCAPE_RELAX) {
                                 /* Invalid escape code, let's take it literal then */
                                 *(t++) = '\\';
                                 continue;
                         }
 
-                        free(r);
-                        return k;
+                        return r;
                 }
 
-                f += k;
+                f += r;
                 if (eight_bit)
                         /* One byte? Set directly as specified */
                         *(t++) = u;
@@ -359,8 +359,9 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
 
         *t = 0;
 
-        *ret = r;
-        return t - r;
+        assert(t >= ans); /* Let static analyzers know that the answer is non-negative. */
+        *ret = TAKE_PTR(ans);
+        return t - *ret;
 }
 
 char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) {
@@ -445,7 +446,6 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
                                                       console_width,
                                                       FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS));
 }
-#endif /* NM_IGNORED */
 
 char* octescape(const char *s, size_t len) {
         char *r, *t;
@@ -546,3 +546,24 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
 
         return str_realloc(buf);
 }
+
+char* quote_command_line(char **argv) {
+        _cleanup_free_ char *result = NULL;
+
+        assert(argv);
+
+        char **a;
+        STRV_FOREACH(a, argv) {
+                _cleanup_free_ char *t = NULL;
+
+                t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY);
+                if (!t)
+                        return NULL;
+
+                if (!strextend_with_separator(&result, " ", t))
+                        return NULL;
+        }
+
+        return TAKE_PTR(result);
+}
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index 907b572b..d490510d 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -45,14 +45,15 @@ char* cescape(const char *s);
 char* cescape_length(const char *s, size_t n);
 int cescape_char(char c, char *buf);
 
-int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
-static inline int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
+
+ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
+static inline ssize_t cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
         return cunescape_length_with_prefix(s, length, NULL, flags, ret);
 }
-static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+static inline ssize_t cunescape(const char *s, UnescapeFlags flags, char **ret) {
         return cunescape_length(s, strlen(s), flags, ret);
 }
-int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
 
 typedef enum XEscapeFlags {
         XESCAPE_8_BIT          = 1 << 0,
@@ -68,3 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
 
 char* shell_escape(const char *s, const char *bad);
 char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
+char* quote_command_line(char **argv);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
index f60e1042..c73dcff1 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -12,13 +12,13 @@
 #include "macro.h"
 #include "string-util.h"
 
-char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) {
+char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) {
         assert(addr);
         assert(buffer);
         assert(addr->length <= HW_ADDR_MAX_SIZE);
 
         for (size_t i = 0; i < addr->length; i++) {
-                sprintf(&buffer[3*i], "%02"PRIx8, addr->addr.bytes[i]);
+                sprintf(&buffer[3*i], "%02"PRIx8, addr->bytes[i]);
                 if (i < addr->length - 1)
                         buffer[3*i + 2] = ':';
         }
@@ -26,6 +26,29 @@ char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_
         return buffer;
 }
 
+int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        r = CMP(a->length, b->length);
+        if (r != 0)
+                return r;
+
+        return memcmp(a->bytes, b->bytes, a->length);
+}
+
+static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
+        assert(p);
+        assert(state);
+
+        siphash24_compress(&p->length, sizeof(p->length), state);
+        siphash24_compress(p->bytes, p->length, state);
+}
+
+DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
+
 char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
         assert(addr);
         assert(buffer);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
index 712c9277..794fc55b 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -11,24 +11,34 @@
  * defines a macro of the same name with a much lower size. */
 #define HW_ADDR_MAX_SIZE 32
 
-union hw_addr_union {
-        struct ether_addr ether;
-        uint8_t infiniband[INFINIBAND_ALEN];
-        uint8_t bytes[HW_ADDR_MAX_SIZE];
-};
-
-typedef struct hw_addr_data {
-        union hw_addr_union addr;
+struct hw_addr_data {
         size_t length;
-} hw_addr_data;
+        union {
+                struct ether_addr ether;
+                uint8_t infiniband[INFINIBAND_ALEN];
+                uint8_t bytes[HW_ADDR_MAX_SIZE];
+        };
+};
 
 #define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
-char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]);
+char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]);
 
-/* Use only as function argument, never stand-alone! */
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 ยง6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
 #define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){})
 
-#define HW_ADDR_NULL ((const hw_addr_data){})
+#define HW_ADDR_NULL ((const struct hw_addr_data){})
+
+int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
+static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
+        return hw_addr_compare(a, b) == 0;
+}
+static inline bool hw_addr_is_null(const struct hw_addr_data *addr) {
+        return hw_addr_equal(addr, &HW_ADDR_NULL);
+}
+
+extern const struct hash_ops hw_addr_hash_ops;
 
 #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
 #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
@@ -36,6 +46,8 @@ char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_
 #define ETHER_ADDR_TO_STRING_MAX (3*6)
 char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
 int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret);
+/* Use only as function argument, never stand-alone! */
+#define ETHER_ADDR_TO_STR(addr) ether_addr_to_string((addr), (char[ETHER_ADDR_TO_STRING_MAX]){})
 
 int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b);
 static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.c b/src/libnm-systemd-shared/src/basic/extract-word.c
index b339eae2..6781fb55 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.c
+++ b/src/libnm-systemd-shared/src/basic/extract-word.c
@@ -29,6 +29,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
 
         assert(p);
         assert(ret);
+        assert(!FLAGS_SET(flags, EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE));
 
         /* Bail early if called after last value or with no input */
         if (!*p)
@@ -52,7 +53,8 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
                         goto finish_force_terminate;
                 else if (strchr(separators, c)) {
                         if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
-                                (*p)++;
+                                if (!(flags & EXTRACT_RETAIN_SEPARATORS))
+                                        (*p)++;
                                 goto finish_force_next;
                         }
                 } else {
@@ -125,48 +127,58 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
                                         return -EINVAL;
                                 } else if (c == quote) {        /* found the end quote */
                                         quote = 0;
-                                        break;
+                                        if (flags & EXTRACT_UNQUOTE)
+                                                break;
                                 } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
                                         backslash = true;
                                         break;
-                                } else {
-                                        if (!GREEDY_REALLOC(s, sz+2))
-                                                return -ENOMEM;
-
-                                        s[sz++] = c;
                                 }
+
+                                if (!GREEDY_REALLOC(s, sz+2))
+                                        return -ENOMEM;
+
+                                s[sz++] = c;
+
+                                if (quote == 0)
+                                        break;
                         }
 
                 } else {
                         for (;; (*p)++, c = **p) {
                                 if (c == 0)
                                         goto finish_force_terminate;
-                                else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) {
+                                else if (IN_SET(c, '\'', '"') && (flags & (EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE))) {
                                         quote = c;
-                                        break;
+                                        if (flags & EXTRACT_UNQUOTE)
+                                                break;
                                 } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
                                         backslash = true;
                                         break;
                                 } else if (strchr(separators, c)) {
                                         if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
-                                                (*p)++;
+                                                if (!(flags & EXTRACT_RETAIN_SEPARATORS))
+                                                        (*p)++;
                                                 goto finish_force_next;
                                         }
-                                        /* Skip additional coalesced separators. */
-                                        for (;; (*p)++, c = **p) {
-                                                if (c == 0)
-                                                        goto finish_force_terminate;
-                                                if (!strchr(separators, c))
-                                                        break;
-                                        }
+                                        if (!(flags & EXTRACT_RETAIN_SEPARATORS))
+                                                /* Skip additional coalesced separators. */
+                                                for (;; (*p)++, c = **p) {
+                                                        if (c == 0)
+                                                                goto finish_force_terminate;
+                                                        if (!strchr(separators, c))
+                                                                break;
+                                                }
                                         goto finish;
 
-                                } else {
-                                        if (!GREEDY_REALLOC(s, sz+2))
-                                                return -ENOMEM;
-
-                                        s[sz++] = c;
                                 }
+
+                                if (!GREEDY_REALLOC(s, sz+2))
+                                        return -ENOMEM;
+
+                                s[sz++] = c;
+
+                                if (quote != 0)
+                                        break;
                         }
                 }
         }
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.h b/src/libnm-systemd-shared/src/basic/extract-word.h
index 0e9e77e9..c82ad761 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.h
+++ b/src/libnm-systemd-shared/src/basic/extract-word.h
@@ -8,9 +8,11 @@ typedef enum ExtractFlags {
         EXTRACT_CUNESCAPE                = 1 << 1, /* Unescape known escape sequences. */
         EXTRACT_UNESCAPE_RELAX           = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */
         EXTRACT_UNESCAPE_SEPARATORS      = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */
-        EXTRACT_UNQUOTE                  = 1 << 4, /* Remove quoting with "" and ''. */
-        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */
-        EXTRACT_RETAIN_ESCAPE            = 1 << 6, /* Treat escape character '\' as any other character without special meaning */
+        EXTRACT_KEEP_QUOTE               = 1 << 4, /* Ignore separators in quoting with "" and ''. */
+        EXTRACT_UNQUOTE                  = 1 << 5, /* Ignore separators in quoting with "" and '', and remove the quotes. */
+        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 6, /* Don't treat multiple adjacent separators as one */
+        EXTRACT_RETAIN_ESCAPE            = 1 << 7, /* Treat escape character '\' as any other character without special meaning */
+        EXTRACT_RETAIN_SEPARATORS        = 1 << 8, /* Do not advance the original string pointer past the separator(s) */
 
         /* Note that if no flags are specified, escaped escape characters will be silently stripped. */
 } ExtractFlags;
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index 670b72ff..ee2a4caf 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -4,19 +4,20 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/btrfs.h>
+#include <linux/magic.h>
+#include <sys/ioctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
-#include "copy.h"
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "io-util.h"
 #include "macro.h"
-#include "memfd-util.h"
 #include "missing_fcntl.h"
 #include "missing_syscall.h"
 #include "parse-util.h"
@@ -210,10 +211,9 @@ static int get_max_fd(void) {
         return (int) (m - 1);
 }
 
-int close_all_fds(const int except[], size_t n_except) {
+int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
         static bool have_close_range = true; /* Assume we live in the future */
         _cleanup_closedir_ DIR *d = NULL;
-        struct dirent *de;
         int r = 0;
 
         assert(n_except == 0 || except);
@@ -229,129 +229,104 @@ int close_all_fds(const int except[], size_t n_except) {
                         /* Close everything. Yay! */
 
                         if (close_range(3, -1, 0) >= 0)
-                                return 1;
+                                return 0;
 
-                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
+                        if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
+                                have_close_range = false;
+                        else
                                 return -errno;
 
-                        have_close_range = false;
                 } else {
-                        _cleanup_free_ int *sorted_malloc = NULL;
-                        size_t n_sorted;
-                        int *sorted;
-
-                        assert(n_except < SIZE_MAX);
-                        n_sorted = n_except + 1;
-
-                        if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
-                                sorted = sorted_malloc = new(int, n_sorted);
-                        else
-                                sorted = newa(int, n_sorted);
-
-                        if (sorted) {
-                                int c = 0;
-
-                                memcpy(sorted, except, n_except * sizeof(int));
+                        typesafe_qsort(except, n_except, cmp_int);
 
-                                /* Let's add fd 2 to the list of fds, to simplify the loop below, as this
-                                 * allows us to cover the head of the array the same way as the body */
-                                sorted[n_sorted-1] = 2;
+                        for (size_t i = 0; i < n_except; i++) {
+                                int start = i == 0 ? 2 : MAX(except[i-1], 2); /* The first three fds shall always remain open */
+                                int end = MAX(except[i], 2);
 
-                                typesafe_qsort(sorted, n_sorted, cmp_int);
+                                assert(end >= start);
 
-                                for (size_t i = 0; i < n_sorted-1; i++) {
-                                        int start, end;
-
-                                        start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
-                                        end = MAX(sorted[i+1], 2);
-
-                                        assert(end >= start);
-
-                                        if (end - start <= 1)
-                                                continue;
-
-                                        /* Close everything between the start and end fds (both of which shall stay open) */
-                                        if (close_range(start + 1, end - 1, 0) < 0) {
-                                                if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
-                                                        return -errno;
+                                if (end - start <= 1)
+                                        continue;
 
+                                /* Close everything between the start and end fds (both of which shall stay open) */
+                                if (close_range(start + 1, end - 1, 0) < 0) {
+                                        if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
                                                 have_close_range = false;
-                                                break;
-                                        }
-
-                                        c += end - start - 1;
+                                        else
+                                                return -errno;
+                                        goto opendir_fallback;
                                 }
+                        }
 
-                                if (have_close_range) {
-                                        /* The loop succeeded. Let's now close everything beyond the end */
+                        /* The loop succeeded. Let's now close everything beyond the end */
 
-                                        if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
-                                                return c;
+                        if (except[n_except-1] >= INT_MAX) /* Don't let the addition below overflow */
+                                return 0;
 
-                                        if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
-                                                return c + 1;
+                        int start = MAX(except[n_except-1], 2);
 
-                                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
-                                                return -errno;
+                        if (close_range(start + 1, -1, 0) >= 0)
+                                return 0;
 
-                                        have_close_range = false;
-                                }
-                        }
+                        if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
+                                have_close_range = false;
+                        else
+                                return -errno;
                 }
-
-                /* Fallback on OOM or if close_range() is not supported */
         }
 
-        d = opendir("/proc/self/fd");
-        if (!d) {
-                int fd, max_fd;
+        /* Fallback for when close_range() is not supported */
+ opendir_fallback:
+        d = allow_alloc ? opendir("/proc/self/fd") : NULL;
+        if (d) {
+                struct dirent *de;
 
-                /* When /proc isn't available (for example in chroots) the fallback is brute forcing through
-                 * the fd table */
+                FOREACH_DIRENT(de, d, return -errno) {
+                        int fd = -1, q;
 
-                max_fd = get_max_fd();
-                if (max_fd < 0)
-                        return max_fd;
+                        if (safe_atoi(de->d_name, &fd) < 0)
+                                /* Let's better ignore this, just in case */
+                                continue;
 
-                /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
-                 * spin the CPU for a long time. */
-                if (max_fd > MAX_FD_LOOP_LIMIT)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
-                                               "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
-                                               max_fd);
+                        if (fd < 3)
+                                continue;
 
-                for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
-                        int q;
+                        if (fd == dirfd(d))
+                                continue;
 
                         if (fd_in_set(fd, except, n_except))
                                 continue;
 
                         q = close_nointr(fd);
-                        if (q < 0 && q != -EBADF && r >= 0)
+                        if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
                                 r = q;
                 }
 
                 return r;
         }
 
-        FOREACH_DIRENT(de, d, return -errno) {
-                int fd = -1, q;
+        /* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by
+         * brute-forcing through the file descriptor table. */
 
-                if (safe_atoi(de->d_name, &fd) < 0)
-                        /* Let's better ignore this, just in case */
-                        continue;
+        int max_fd = get_max_fd();
+        if (max_fd < 0)
+                return max_fd;
 
-                if (fd < 3)
-                        continue;
+        /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
+         * spin the CPU for a long time. */
+        if (max_fd > MAX_FD_LOOP_LIMIT)
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
+                                       "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
+                                       max_fd);
 
-                if (fd == dirfd(d))
-                        continue;
+        for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
+                int q;
 
                 if (fd_in_set(fd, except, n_except))
                         continue;
 
                 q = close_nointr(fd);
-                if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
+                if (q < 0 && q != -EBADF && r >= 0)
                         r = q;
         }
 
@@ -463,11 +438,9 @@ bool fdname_is_valid(const char *s) {
 
 #if 0 /* NM_IGNORED */
 int fd_get_path(int fd, char **ret) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         int r;
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        r = readlink_malloc(procfs_path, ret);
+        r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
         if (r == -ENOENT) {
                 /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
                  * things debuggable and distinguish the two. */
@@ -524,343 +497,6 @@ int move_fd(int from, int to, int cloexec) {
 
         return to;
 }
-
-int acquire_data_fd(const void *data, size_t size, unsigned flags) {
-
-        _cleanup_close_pair_ int pipefds[2] = { -1, -1 };
-        char pattern[] = "/dev/shm/data-fd-XXXXXX";
-        _cleanup_close_ int fd = -1;
-        int isz = 0, r;
-        ssize_t n;
-        off_t f;
-
-        assert(data || size == 0);
-
-        /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
-         * complex than I wish it was. But here's why:
-         *
-         * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
-         *    read-only. Unfortunately they require kernel 3.17, and โ€“ at the time of writing โ€“ we still support 3.14.
-         *
-         * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
-         *    a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
-         *    clients can only bump their size to a system-wide limit, which might be quite low.
-         *
-         * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
-         *    earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
-         *    /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
-         *
-         * d) Finally, we try creating a regular file in /dev/shm, which we then delete.
-         *
-         * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
-         * figure. */
-
-        if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
-                /* As a special case, return /dev/null if we have been called for an empty data block */
-                r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (r < 0)
-                        return -errno;
-
-                return r;
-        }
-
-        if ((flags & ACQUIRE_NO_MEMFD) == 0) {
-                fd = memfd_new("data-fd");
-                if (fd < 0)
-                        goto try_pipe;
-
-                n = write(fd, data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                f = lseek(fd, 0, SEEK_SET);
-                if (f != 0)
-                        return -errno;
-
-                r = memfd_set_sealed(fd);
-                if (r < 0)
-                        return r;
-
-                return TAKE_FD(fd);
-        }
-
-try_pipe:
-        if ((flags & ACQUIRE_NO_PIPE) == 0) {
-                if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
-                        return -errno;
-
-                isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                if (isz < 0)
-                        return -errno;
-
-                if ((size_t) isz < size) {
-                        isz = (int) size;
-                        if (isz < 0 || (size_t) isz != size)
-                                return -E2BIG;
-
-                        /* Try to bump the pipe size */
-                        (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
-
-                        /* See if that worked */
-                        isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                        if (isz < 0)
-                                return -errno;
-
-                        if ((size_t) isz < size)
-                                goto try_dev_shm;
-                }
-
-                n = write(pipefds[1], data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                (void) fd_nonblock(pipefds[0], false);
-
-                return TAKE_FD(pipefds[0]);
-        }
-
-try_dev_shm:
-        if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
-                fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
-                if (fd < 0)
-                        goto try_dev_shm_without_o_tmpfile;
-
-                n = write(fd, data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
-                return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
-        }
-
-try_dev_shm_without_o_tmpfile:
-        if ((flags & ACQUIRE_NO_REGULAR) == 0) {
-                fd = mkostemp_safe(pattern);
-                if (fd < 0)
-                        return fd;
-
-                n = write(fd, data, size);
-                if (n < 0) {
-                        r = -errno;
-                        goto unlink_and_return;
-                }
-                if ((size_t) n != size) {
-                        r = -EIO;
-                        goto unlink_and_return;
-                }
-
-                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
-                r = open(pattern, O_RDONLY|O_CLOEXEC);
-                if (r < 0)
-                        r = -errno;
-
-        unlink_and_return:
-                (void) unlink(pattern);
-                return r;
-        }
-
-        return -EOPNOTSUPP;
-}
-
-/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
-#define DATA_FD_MEMORY_LIMIT (64U*1024U)
-
-/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
-#define DATA_FD_TMP_LIMIT (1024U*1024U)
-
-int fd_duplicate_data_fd(int fd) {
-
-        _cleanup_close_ int copy_fd = -1, tmp_fd = -1;
-        _cleanup_free_ void *remains = NULL;
-        size_t remains_size = 0;
-        const char *td;
-        struct stat st;
-        int r;
-
-        /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
-         * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
-         * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
-         * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
-         * /var/tmp. */
-
-        if (fstat(fd, &st) < 0)
-                return -errno;
-
-        /* For now, let's only accept regular files, sockets, pipes and char devices */
-        if (S_ISDIR(st.st_mode))
-                return -EISDIR;
-        if (S_ISLNK(st.st_mode))
-                return -ELOOP;
-        if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
-                return -EBADFD;
-
-        /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
-         * that we use the reported regular file size only as a hint, given that there are plenty special files in
-         * /proc and /sys which report a zero file size but can be read from. */
-
-        if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
-
-                /* Try a memfd first */
-                copy_fd = memfd_new("data-fd");
-                if (copy_fd >= 0) {
-                        off_t f;
-
-                        r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
-                        if (r < 0)
-                                return r;
-
-                        f = lseek(copy_fd, 0, SEEK_SET);
-                        if (f != 0)
-                                return -errno;
-
-                        if (r == 0) {
-                                /* Did it fit into the limit? If so, we are done. */
-                                r = memfd_set_sealed(copy_fd);
-                                if (r < 0)
-                                        return r;
-
-                                return TAKE_FD(copy_fd);
-                        }
-
-                        /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
-
-                } else {
-                        _cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
-                        int isz;
-
-                        /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
-                         * then block indefinitely when we hit the pipe size limit */
-
-                        if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
-                                return -errno;
-
-                        isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                        if (isz < 0)
-                                return -errno;
-
-                        /* Try to enlarge the pipe size if necessary */
-                        if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
-
-                                (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
-
-                                isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                                if (isz < 0)
-                                        return -errno;
-                        }
-
-                        if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
-
-                                r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
-                                if (r < 0 && r != -EAGAIN)
-                                        return r; /* If we get EAGAIN it could be because of the source or because of
-                                                   * the destination fd, we can't know, as sendfile() and friends won't
-                                                   * tell us. Hence, treat this as reason to fall back, just to be
-                                                   * sure. */
-                                if (r == 0) {
-                                        /* Everything fit in, yay! */
-                                        (void) fd_nonblock(pipefds[0], false);
-
-                                        return TAKE_FD(pipefds[0]);
-                                }
-
-                                /* Things didn't fit in. But we read data into the pipe, let's remember that, so that
-                                 * when writing the new file we incorporate this first. */
-                                copy_fd = TAKE_FD(pipefds[0]);
-                        }
-                }
-        }
-
-        /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
-        if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
-            (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
-                off_t f;
-
-                tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
-                if (tmp_fd < 0)
-                        return tmp_fd;
-
-                if (copy_fd >= 0) {
-                        /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
-                         * temporary file first. */
-
-                        r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
-                        if (r < 0)
-                                return r;
-
-                        assert(r == 0);
-                }
-
-                if (remains_size > 0) {
-                        /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
-                         * failed copy operation, let's flush them out next. */
-
-                        r = loop_write(tmp_fd, remains, remains_size, false);
-                        if (r < 0)
-                                return r;
-                }
-
-                r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        goto finish;  /* Yay, it fit in */
-
-                /* It didn't fit in. Let's not forget to use what we already used */
-                f = lseek(tmp_fd, 0, SEEK_SET);
-                if (f != 0)
-                        return -errno;
-
-                CLOSE_AND_REPLACE(copy_fd, tmp_fd);
-
-                remains = mfree(remains);
-                remains_size = 0;
-        }
-
-        /* As last fallback use /var/tmp */
-        r = var_tmp_dir(&td);
-        if (r < 0)
-                return r;
-
-        tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
-        if (tmp_fd < 0)
-                return tmp_fd;
-
-        if (copy_fd >= 0) {
-                /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
-                 * into the temporary file first. */
-                r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
-                if (r < 0)
-                        return r;
-
-                assert(r == 0);
-        }
-
-        if (remains_size > 0) {
-                /* Then, copy in any read but not yet written bytes. */
-                r = loop_write(tmp_fd, remains, remains_size, false);
-                if (r < 0)
-                        return r;
-        }
-
-        /* Copy in the rest */
-        r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
-        if (r < 0)
-                return r;
-
-        assert(r == 0);
-
-finish:
-        /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
-         * file again */
-
-        return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
-}
 #endif /* NM_IGNORED */
 
 int fd_move_above_stdio(int fd) {
@@ -1016,7 +652,6 @@ finish:
 }
 
 int fd_reopen(int fd, int flags) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         int new_fd;
 
         /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
@@ -1026,8 +661,7 @@ int fd_reopen(int fd, int flags) {
          *
          * This implicitly resets the file read index to 0. */
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        new_fd = open(procfs_path, flags);
+        new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
         if (new_fd < 0) {
                 if (errno != ENOENT)
                         return -errno;
@@ -1064,4 +698,21 @@ int read_nr_open(void) {
         /* If we fail, fall back to the hard-coded kernel limit of 1024 * 1024. */
         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;
+
+        if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
+                return -errno;
+
+        return 0;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index aa8e082b..2382d52d 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -7,6 +7,7 @@
 #include <sys/socket.h>
 
 #include "macro.h"
+#include "stdio-util.h"
 
 /* maximum length of fdname */
 #define FDNAME_MAX 255
@@ -56,7 +57,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
 int fd_nonblock(int fd, bool nonblock);
 int fd_cloexec(int fd, bool cloexec);
 
-int close_all_fds(const int except[], size_t n_except);
+int close_all_fds_full(int except[], size_t n_except, bool allow_alloc);
+static inline int close_all_fds(int except[], size_t n_except) {
+        return close_all_fds_full(except, n_except, true);
+}
 
 int same_fd(int a, int b);
 
@@ -76,10 +80,6 @@ enum {
         ACQUIRE_NO_REGULAR  = 1 << 4,
 };
 
-int acquire_data_fd(const void *data, size_t size, unsigned flags);
-
-int fd_duplicate_data_fd(int fd);
-
 int fd_move_above_stdio(int fd);
 
 int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
@@ -105,7 +105,20 @@ static inline int make_null_stdio(void) {
                 0;                              \
         })
 
-
 int fd_reopen(int fd, int flags);
-
 int read_nr_open(void);
+int btrfs_defrag_fd(int fd);
+
+/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
+#define PROC_FD_PATH_MAX \
+        (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
+
+static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
+        assert(buf);
+        assert(fd >= 0);
+        assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd));
+        return buf;
+}
+
+#define FORMAT_PROC_FD_PATH(fd) \
+        format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index de78f70e..322638a2 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -15,6 +15,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "chase-symlinks.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -27,10 +28,22 @@
 #include "socket-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
+#include "sync-util.h"
 #include "tmpfile-util.h"
 
-/* The maximum size of the file we'll read in one go. */
-#define READ_FULL_BYTES_MAX (4U*1024U*1024U - 1)
+/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
+#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
+
+/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
+ * in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
+ * limit. This reflects the fact that we use different strategies for reading virtual and regular files:
+ * virtual files we generally have to read in a single read() syscall since the kernel doesn't support
+ * continuation read()s for them. Thankfully they are somewhat size constrained. Thus we can allocate the
+ * full potential buffer in advance. Regular files OTOH can be much larger, and there we grow the allocations
+ * exponentially in a loop. We use a size limit of 4M-2 because 4M-1 is the maximum buffer that /proc/sys/
+ * allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we
+ * can detect EOFs. */
+#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U)
 
 int fopen_unlocked(const char *path, const char *options, FILE **ret) {
         assert(ret);
@@ -140,6 +153,30 @@ int write_string_stream_ts(
                         return -EBADF;
         }
 
+        if (flags & WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) {
+                _cleanup_free_ char *t = NULL;
+
+                /* If value to be written is same as that of the existing value, then suppress the write. */
+
+                if (fd < 0) {
+                        fd = fileno(f);
+                        if (fd < 0)
+                                return -EBADF;
+                }
+
+                /* Read an additional byte to detect cases where the prefix matches but the rest
+                 * doesn't. Also, 0 returned by read_virtual_file_fd() means the read was truncated and
+                 * it won't be equal to the new value. */
+                if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 &&
+                    streq_skip_trailing_chars(line, t, NEWLINE)) {
+                        log_debug("No change in value '%s', supressing write", line);
+                        return 0;
+                }
+
+                if (lseek(fd, 0, SEEK_SET) < 0)
+                        return -errno;
+        }
+
         needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
 
         if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
@@ -255,10 +292,11 @@ int write_string_file_ts(
                 assert(!ts);
 
         /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
-        fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
+        fd = open(fn, O_CLOEXEC|O_NOCTTY |
                   (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
                   (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
-                  (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0),
+                  (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
+                  (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
                   (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
         if (fd < 0) {
                 r = -errno;
@@ -368,9 +406,8 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
 }
 #endif /* NM_IGNORED */
 
-int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
+int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
         _cleanup_free_ char *buf = NULL;
-        _cleanup_close_ int fd = -1;
         size_t n, size;
         int n_retries;
         bool truncated = false;
@@ -388,11 +425,8 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
          * contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
          * partial success, 1 if untruncated contents were read. */
 
-        fd = open(filename, O_RDONLY|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        assert(max_size <= READ_FULL_BYTES_MAX || max_size == SIZE_MAX);
+        assert(fd >= 0);
+        assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX);
 
         /* Limit the number of attempts to read the number of bytes returned by fstat(). */
         n_retries = 3;
@@ -407,7 +441,7 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
                         return -EBADF;
 
                 /* Be prepared for files from /proc which generally report a file size of 0. */
-                assert_cc(READ_FULL_BYTES_MAX < SSIZE_MAX);
+                assert_cc(READ_VIRTUAL_BYTES_MAX < SSIZE_MAX);
                 if (st.st_size > 0 && n_retries > 1) {
                         /* Let's use the file size if we have more than 1 attempt left. On the last attempt
                          * we'll ignore the file size */
@@ -421,13 +455,18 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
                         } else {
                                 size = MIN((size_t) st.st_size, max_size);
 
-                                if (size > READ_FULL_BYTES_MAX)
+                                if (size > READ_VIRTUAL_BYTES_MAX)
                                         return -EFBIG;
                         }
 
                         n_retries--;
+                } else if (n_retries > 1) {
+                        /* Files in /proc are generally smaller than the page size so let's start with
+                         * a page size buffer from malloc and only use the max buffer on the final try. */
+                        size = MIN3(page_size() - 1, READ_VIRTUAL_BYTES_MAX, max_size);
+                        n_retries = 1;
                 } else {
-                        size = MIN(READ_FULL_BYTES_MAX, max_size);
+                        size = MIN(READ_VIRTUAL_BYTES_MAX, max_size);
                         n_retries = 0;
                 }
 
@@ -436,7 +475,7 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
                         return -ENOMEM;
 
                 /* Use a bigger allocation if we got it anyway, but not more than the limit. */
-                size = MIN3(MALLOC_SIZEOF_SAFE(buf) - 1, max_size, READ_FULL_BYTES_MAX);
+                size = MIN3(MALLOC_SIZEOF_SAFE(buf) - 1, max_size, READ_VIRTUAL_BYTES_MAX);
 
                 for (;;) {
                         ssize_t k;
@@ -458,9 +497,14 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
                 if (n <= size)
                         break;
 
-                /* If a maximum size is specified and we already read as much, no need to try again */
-                if (max_size != SIZE_MAX && n >= max_size) {
-                        n = max_size;
+                /* If a maximum size is specified and we already read more we know the file is larger, and
+                 * can handle this as truncation case. Note that if the size of what we read equals the
+                 * maximum size then this doesn't mean truncation, the file might or might not end on that
+                 * byte. We need to rerun the loop in that case, with a larger buffer size, so that we read
+                 * at least one more byte to be able to distinguish EOF from truncation. */
+                if (max_size != SIZE_MAX && n > max_size) {
+                        n = size; /* Make sure we never use more than what we sized the buffer for (so that
+                                   * we have one free byte in it for the trailing NUL we add below).*/
                         truncated = true;
                         break;
                 }
@@ -507,6 +551,18 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
         return !truncated;
 }
 
+int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
+        _cleanup_close_ int fd = -1;
+
+        assert(filename);
+
+        fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        return read_virtual_file_fd(fd, max_size, ret_contents, ret_size);
+}
+
 int read_full_stream_full(
                 FILE *f,
                 const char *filename,
@@ -517,18 +573,17 @@ int read_full_stream_full(
                 size_t *ret_size) {
 
         _cleanup_free_ char *buf = NULL;
-        size_t n, n_next, l;
+        size_t n, n_next = 0, l;
         int fd, r;
 
         assert(f);
         assert(ret_contents);
         assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
+        assert(size != SIZE_MAX || !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER));
 
-        if (offset != UINT64_MAX && offset > LONG_MAX)
+        if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
                 return -ERANGE;
 
-        n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */
-
         fd = fileno(f);
         if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
                         * fmemopen()), let's optimize our buffering */
@@ -538,20 +593,20 @@ int read_full_stream_full(
                         return -errno;
 
                 if (S_ISREG(st.st_mode)) {
-                        if (size == SIZE_MAX) {
+
+                        /* Try to start with the right file size if we shall read the file in full. Note
+                         * that we increase the size to read here by one, so that the first read attempt
+                         * already makes us notice the EOF. If the reported size of the file is zero, we
+                         * avoid this logic however, since quite likely it might be a virtual file in procfs
+                         * that all report a zero file size. */
+
+                        if (st.st_size > 0 &&
+                            (size == SIZE_MAX || FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER))) {
+
                                 uint64_t rsize =
                                         LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset);
 
-                                /* Safety check */
-                                if (rsize > READ_FULL_BYTES_MAX)
-                                        return -E2BIG;
-
-                                /* Start with the right file size. Note that we increase the size to read
-                                 * here by one, so that the first read attempt already makes us notice the
-                                 * EOF. If the reported size of the file is zero, we avoid this logic
-                                 * however, since quite likely it might be a virtual file in procfs that all
-                                 * report a zero file size. */
-                                if (st.st_size > 0)
+                                if (rsize < SIZE_MAX) /* overflow check */
                                         n_next = rsize + 1;
                         }
 
@@ -560,6 +615,17 @@ int read_full_stream_full(
                 }
         }
 
+        /* If we don't know how much to read, figure it out now. If we shall read a part of the file, then
+         * allocate the requested size. If we shall load the full file start with LINE_MAX. Note that if
+         * READ_FULL_FILE_FAIL_WHEN_LARGER we consider the specified size a safety limit, and thus also start
+         * with LINE_MAX, under assumption the file is most likely much shorter. */
+        if (n_next == 0)
+                n_next = size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) ? size : LINE_MAX;
+
+        /* Never read more than we need to determine that our own limit is hit */
+        if (n_next > READ_FULL_BYTES_MAX)
+                n_next = READ_FULL_BYTES_MAX + 1;
+
         if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0)
                 return -errno;
 
@@ -568,6 +634,11 @@ int read_full_stream_full(
                 char *t;
                 size_t k;
 
+                /* If we shall fail when reading overly large data, then read exactly one byte more than the
+                 * specified size at max, since that'll tell us if there's anymore data beyond the limit*/
+                if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
+                        n_next = size + 1;
+
                 if (flags & READ_FULL_FILE_SECURE) {
                         t = malloc(n_next + 1);
                         if (!t) {
@@ -576,7 +647,7 @@ int read_full_stream_full(
                         }
                         memcpy_safe(t, buf, n);
                         explicit_bzero_safe(buf, n);
-                        buf = mfree(buf);
+                        free(buf);
                 } else {
                         t = realloc(buf, n_next + 1);
                         if (!t)
@@ -601,14 +672,18 @@ int read_full_stream_full(
                 if (feof(f))
                         break;
 
-                if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
+                if (size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER)) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
                         assert(l == size);
                         break;
                 }
 
                 assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
 
-                /* Safety check */
+                if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > size) {
+                        r = -E2BIG;
+                        goto finalize;
+                }
+
                 if (n >= READ_FULL_BYTES_MAX) {
                         r = -E2BIG;
                         goto finalize;
@@ -697,8 +772,6 @@ int read_full_file_full(
                 if (dir_fd == AT_FDCWD)
                         r = sockaddr_un_set_path(&sa.un, filename);
                 else {
-                        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
                         /* If we shall operate relative to some directory, then let's use O_PATH first to
                          * open the socket inode, and then connect to it via /proc/self/fd/. We have to do
                          * this since there's not connectat() that takes a directory fd as first arg. */
@@ -707,8 +780,7 @@ int read_full_file_full(
                         if (dfd < 0)
                                 return -errno;
 
-                        xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
-                        r = sockaddr_un_set_path(&sa.un, procfs_path);
+                        r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
                 }
                 if (r < 0)
                         return r;
@@ -875,10 +947,12 @@ DIR *xopendirat(int fd, const char *name, int flags) {
         return d;
 }
 
-static int mode_to_flags(const char *mode) {
+int fopen_mode_to_flags(const char *mode) {
         const char *p;
         int flags;
 
+        assert(mode);
+
         if ((p = startswith(mode, "r+")))
                 flags = O_RDWR;
         else if ((p = startswith(mode, "r")))
@@ -931,7 +1005,7 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
         } else {
                 int fd, mode_flags;
 
-                mode_flags = mode_to_flags(mode);
+                mode_flags = fopen_mode_to_flags(mode);
                 if (mode_flags < 0)
                         return mode_flags;
 
@@ -1071,39 +1145,6 @@ int search_and_fopen_nulstr(
 
         return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
 }
-
-int chase_symlinks_and_fopen_unlocked(
-                const char *path,
-                const char *root,
-                unsigned chase_flags,
-                const char *open_flags,
-                FILE **ret_file,
-                char **ret_path) {
-
-        _cleanup_close_ int fd = -1;
-        _cleanup_free_ char *final_path = NULL;
-        int mode_flags, r;
-
-        assert(path);
-        assert(open_flags);
-        assert(ret_file);
-
-        mode_flags = mode_to_flags(open_flags);
-        if (mode_flags < 0)
-                return mode_flags;
-
-        fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL);
-        if (fd < 0)
-                return fd;
-
-        r = take_fdopen_unlocked(&fd, open_flags, ret_file);
-        if (r < 0)
-                return r;
-
-        if (ret_path)
-                *ret_path = TAKE_PTR(final_path);
-        return 0;
-}
 #endif /* NM_IGNORED */
 
 int fflush_and_check(FILE *f) {
@@ -1134,10 +1175,7 @@ int fflush_sync_and_check(FILE *f) {
         if (fd < 0)
                 return 0;
 
-        if (fsync(fd) < 0)
-                return -errno;
-
-        r = fsync_directory_of_file(fd);
+        r = fsync_full(fd);
         if (r < 0)
                 return r;
 
@@ -1409,18 +1447,3 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
                             filename, st->st_mode & 07777);
         return 0;
 }
-
-#if 0 /* NM_IGNORED */
-int rename_and_apply_smack_floor_label(const char *from, const char *to) {
-        int r = 0;
-        if (rename(from, to) < 0)
-                return -errno;
-
-#if HAVE_SMACK_RUN_LABEL
-        r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
-        if (r < 0)
-                return r;
-#endif
-        return r;
-}
-#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index 1a8b7dec..899def94 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -2,15 +2,11 @@
 #pragma once
 
 #include <dirent.h>
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <sys/stat.h>
-#if 0 /* NM_IGNORED */
-#include <sys/fcntl.h>
-#else /* NM_IGNORED */
-#include <fcntl.h>
-#endif /* NM_IGNORED */
 #include <sys/types.h>
 
 #include "macro.h"
@@ -19,17 +15,18 @@
 #define LONG_LINE_MAX (1U*1024U*1024U)
 
 typedef enum {
-        WRITE_STRING_FILE_CREATE                = 1 << 0,
-        WRITE_STRING_FILE_TRUNCATE              = 1 << 1,
-        WRITE_STRING_FILE_ATOMIC                = 1 << 2,
-        WRITE_STRING_FILE_AVOID_NEWLINE         = 1 << 3,
-        WRITE_STRING_FILE_VERIFY_ON_FAILURE     = 1 << 4,
-        WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
-        WRITE_STRING_FILE_SYNC                  = 1 << 6,
-        WRITE_STRING_FILE_DISABLE_BUFFER        = 1 << 7,
-        WRITE_STRING_FILE_NOFOLLOW              = 1 << 8,
-        WRITE_STRING_FILE_MKDIR_0755            = 1 << 9,
-        WRITE_STRING_FILE_MODE_0600             = 1 << 10,
+        WRITE_STRING_FILE_CREATE                     = 1 << 0,
+        WRITE_STRING_FILE_TRUNCATE                   = 1 << 1,
+        WRITE_STRING_FILE_ATOMIC                     = 1 << 2,
+        WRITE_STRING_FILE_AVOID_NEWLINE              = 1 << 3,
+        WRITE_STRING_FILE_VERIFY_ON_FAILURE          = 1 << 4,
+        WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE      = 1 << 5,
+        WRITE_STRING_FILE_SYNC                       = 1 << 6,
+        WRITE_STRING_FILE_DISABLE_BUFFER             = 1 << 7,
+        WRITE_STRING_FILE_NOFOLLOW                   = 1 << 8,
+        WRITE_STRING_FILE_MKDIR_0755                 = 1 << 9,
+        WRITE_STRING_FILE_MODE_0600                  = 1 << 10,
+        WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
 
         /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
            more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
@@ -43,6 +40,7 @@ typedef enum {
         READ_FULL_FILE_UNHEX               = 1 << 2, /* hex decode what we read */
         READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */
         READ_FULL_FILE_CONNECT_SOCKET      = 1 << 4, /* if socket inode, connect to it and read off it */
+        READ_FULL_FILE_FAIL_WHEN_LARGER    = 1 << 5, /* fail loading if file is larger than specified size */
 } ReadFullFileFlags;
 
 int fopen_unlocked(const char *path, const char *options, FILE **ret);
@@ -70,6 +68,7 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
         return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
 }
 
+int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
 int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
 static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
         return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
@@ -92,14 +91,6 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
 int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
 int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
 
-int chase_symlinks_and_fopen_unlocked(
-                const char *path,
-                const char *root,
-                unsigned chase_flags,
-                const char *open_flags,
-                FILE **ret_file,
-                char **ret_path);
-
 int fflush_and_check(FILE *f);
 int fflush_sync_and_check(FILE *f);
 
@@ -128,4 +119,4 @@ int safe_fgetc(FILE *f, char *ret);
 
 int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
 
-int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path);
+int fopen_mode_to_flags(const char *mode);
diff --git a/src/libnm-systemd-shared/src/basic/format-util.c b/src/libnm-systemd-shared/src/basic/format-util.c
index 398a1815..ebe52713 100644
--- a/src/libnm-systemd-shared/src/basic/format-util.c
+++ b/src/libnm-systemd-shared/src/basic/format-util.c
@@ -5,23 +5,43 @@
 #include "format-util.h"
 #include "memory-util.h"
 #include "stdio-util.h"
+#include "strxcpyx.h"
+
+assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
+int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
+        if (ifindex <= 0)
+                return -EINVAL;
 
-assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1);
-char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) {
-        /* Buffer is always cleared */
-        memzero(buf, IF_NAMESIZE + 1);
         if (if_indextoname(ifindex, buf))
-                return buf;
+                return 0;
 
         if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
-                return NULL;
+                return -errno;
 
         if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
-                snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
+                assert(snprintf_ok(buf, IF_NAMESIZE, "%%%d", ifindex));
         else
-                snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
+                assert(snprintf_ok(buf, IF_NAMESIZE, "%d", ifindex));
 
-        return buf;
+        return 0;
+}
+
+int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) {
+        char buf[IF_NAMESIZE], *copy;
+        int r;
+
+        assert(ret);
+
+        r = format_ifname_full(ifindex, flag, buf);
+        if (r < 0)
+                return r;
+
+        copy = strdup(buf);
+        if (!copy)
+                return -ENOMEM;
+
+        *ret = copy;
+        return 0;
 }
 
 char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
@@ -58,23 +78,23 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
         for (size_t i = 0; i < n; i++)
                 if (t >= table[i].factor) {
                         if (flag & FORMAT_BYTES_BELOW_POINT) {
-                                snprintf(buf, l,
-                                         "%" PRIu64 ".%" PRIu64 "%s",
-                                         t / table[i].factor,
-                                         i != n - 1 ?
-                                         (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
-                                         (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
-                                         table[i].suffix);
+                                (void) snprintf(buf, l,
+                                                "%" PRIu64 ".%" PRIu64 "%s",
+                                                t / table[i].factor,
+                                                i != n - 1 ?
+                                                (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
+                                                (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
+                                                table[i].suffix);
                         } else
-                                snprintf(buf, l,
-                                         "%" PRIu64 "%s",
-                                         t / table[i].factor,
-                                         table[i].suffix);
+                                (void) snprintf(buf, l,
+                                                "%" PRIu64 "%s",
+                                                t / table[i].factor,
+                                                table[i].suffix);
 
                         goto finish;
                 }
 
-        snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
+        (void) snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
 
 finish:
         buf[l-1] = 0;
diff --git a/src/libnm-systemd-shared/src/basic/format-util.h b/src/libnm-systemd-shared/src/basic/format-util.h
index b7e18768..7dd422b9 100644
--- a/src/libnm-systemd-shared/src/basic/format-util.h
+++ b/src/libnm-systemd-shared/src/basic/format-util.h
@@ -61,11 +61,24 @@ typedef enum {
         FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
 } FormatIfnameFlag;
 
-char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag);
-static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
-        return format_ifname_full(ifindex, buf, 0);
+int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]);
+int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret);
+
+static inline int format_ifname(int ifindex, char buf[static IF_NAMESIZE]) {
+        return format_ifname_full(ifindex, 0, buf);
+}
+static inline int format_ifname_alloc(int ifindex, char **ret) {
+        return format_ifname_full_alloc(ifindex, 0, ret);
+}
+
+static inline char *_format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
+        (void) format_ifname_full(ifindex, flag, buf);
+        return buf;
 }
 
+#define FORMAT_IFNAME_FULL(index, flag) _format_ifname_full(index, flag, (char[IF_NAMESIZE]){})
+#define FORMAT_IFNAME(index) _format_ifname_full(index, 0, (char[IF_NAMESIZE]){})
+
 typedef enum {
         FORMAT_BYTES_USE_IEC     = 1 << 0,
         FORMAT_BYTES_BELOW_POINT = 1 << 1,
@@ -74,16 +87,17 @@ typedef enum {
 
 #define FORMAT_BYTES_MAX 16U
 
-char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag);
+char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) _warn_unused_result_;
 
+_warn_unused_result_
 static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
         return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
 }
 
-static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) {
-        if (t == CGROUP_LIMIT_MAX) {
-                (void) snprintf(buf, l, "%s", "infinity");
-                return buf;
-        }
-        return format_bytes(buf, l, t);
-}
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 ยง6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
+#define FORMAT_BYTES(t) format_bytes((char[FORMAT_BYTES_MAX]){}, FORMAT_BYTES_MAX, t)
+#define FORMAT_BYTES_FULL(t, flag) format_bytes_full((char[FORMAT_BYTES_MAX]){}, FORMAT_BYTES_MAX, t, flag)
+
+#define FORMAT_BYTES_CGROUP_PROTECTION(t) (t == CGROUP_LIMIT_MAX ? "infinity" : FORMAT_BYTES(t))
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index ee8eec90..3f309868 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -10,12 +10,10 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
-#include "blockdev-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
-#include "locale-util.h"
 #include "log.h"
 #include "macro.h"
 #include "missing_fcntl.h"
@@ -49,50 +47,39 @@ int unlink_noerrno(const char *path) {
 
 #if 0 /* NM_IGNORED */
 int rmdir_parents(const char *path, const char *stop) {
-        size_t l;
-        int r = 0;
+        char *p;
+        int r;
 
         assert(path);
         assert(stop);
 
-        l = strlen(path);
-
-        /* Skip trailing slashes */
-        while (l > 0 && path[l-1] == '/')
-                l--;
+        if (!path_is_safe(path))
+                return -EINVAL;
 
-        while (l > 0) {
-                char *t;
+        if (!path_is_safe(stop))
+                return -EINVAL;
 
-                /* Skip last component */
-                while (l > 0 && path[l-1] != '/')
-                        l--;
+        p = strdupa(path);
 
-                /* Skip trailing slashes */
-                while (l > 0 && path[l-1] == '/')
-                        l--;
+        for (;;) {
+                char *slash = NULL;
 
-                if (l <= 0)
-                        break;
+                /* skip the last component. */
+                r = path_find_last_component(p, /* accept_dot_dot= */ false, (const char **) &slash, NULL);
+                if (r <= 0)
+                        return r;
+                if (slash == p)
+                        return 0;
 
-                t = strndup(path, l);
-                if (!t)
-                        return -ENOMEM;
+                assert(*slash == '/');
+                *slash = '\0';
 
-                if (path_startswith(stop, t)) {
-                        free(t);
+                if (path_startswith_full(stop, p, /* accept_dot_dot= */ false))
                         return 0;
-                }
-
-                r = rmdir(t);
-                free(t);
 
-                if (r < 0)
-                        if (errno != ENOENT)
-                                return -errno;
+                if (rmdir(p) < 0 && errno != ENOENT)
+                        return -errno;
         }
-
-        return 0;
 }
 
 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
@@ -314,14 +301,11 @@ int fchmod_umask(int fd, mode_t m) {
 }
 
 int fchmod_opath(int fd, mode_t m) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
         /* This function operates also on fd that might have been opened with
          * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
          * fchownat() does. */
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        if (chmod(procfs_path, m) < 0) {
+        if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
@@ -335,12 +319,9 @@ int fchmod_opath(int fd, mode_t m) {
 }
 
 int futimens_opath(int fd, const struct timespec ts[2]) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
         /* Similar to fchmod_path() but for futimens() */
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) {
+        if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
@@ -386,9 +367,8 @@ int fd_warn_permissions(const char *path, int fd) {
 }
 
 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
-        char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         _cleanup_close_ int fd = -1;
-        int r, ret = 0;
+        int r, ret;
 
         assert(path);
 
@@ -418,8 +398,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
         /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
          * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object โ€” which is
          * something fchown(), fchmod(), futimensat() don't allow. */
-        xsprintf(fdpath, "/proc/self/fd/%i", fd);
-
         ret = fchmod_and_chown(fd, mode, uid, gid);
 
         if (stamp != USEC_INFINITY) {
@@ -427,11 +405,11 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
 
                 timespec_store(&ts[0], stamp);
                 ts[1] = ts[0];
-                r = utimensat(AT_FDCWD, fdpath, ts, 0);
+                r = futimens_opath(fd, ts);
         } else
-                r = utimensat(AT_FDCWD, fdpath, NULL, 0);
+                r = futimens_opath(fd, NULL);
         if (r < 0 && ret >= 0)
-                return -errno;
+                return r;
 
         return ret;
 }
@@ -586,8 +564,6 @@ int get_files_in_directory(const char *path, char ***list) {
                 return -errno;
 
         FOREACH_DIRENT_ALL(de, d, return -errno) {
-                dirent_ensure_type(d, de);
-
                 if (!dirent_is_file(de))
                         continue;
 
@@ -713,544 +689,11 @@ int unlink_or_warn(const char *filename) {
 
         return 0;
 }
-#endif /* NM_IGNORED */
-
-int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
-        char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
-        int wd;
-
-        /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
-        xsprintf(path, "/proc/self/fd/%i", what);
-
-        wd = inotify_add_watch(fd, path, mask);
-        if (wd < 0)
-                return -errno;
-
-        return wd;
-}
-
-#if 0 /* NM_IGNORED */
-int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
-        int wd;
-
-        wd = inotify_add_watch(fd, pathname, mask);
-        if (wd < 0) {
-                if (errno == ENOSPC)
-                        return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname);
-
-                return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname);
-        }
-
-        return wd;
-}
-
-static bool unsafe_transition(const struct stat *a, const struct stat *b) {
-        /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
-         * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
-         * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
-
-        if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
-                return false;
-
-        return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
-}
-
-static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
-        _cleanup_free_ char *n1 = NULL, *n2 = NULL;
-
-        if (!FLAGS_SET(flags, CHASE_WARN))
-                return -ENOLINK;
-
-        (void) fd_get_path(a, &n1);
-        (void) fd_get_path(b, &n2);
-
-        return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
-                                 "Detected unsafe path transition %s %s %s during canonicalization of %s.",
-                                 strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path);
-}
-
-static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
-        _cleanup_free_ char *n1 = NULL;
-
-        if (!FLAGS_SET(flags, CHASE_WARN))
-                return -EREMOTE;
-
-        (void) fd_get_path(fd, &n1);
-
-        return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
-                                 "Detected autofs mount point %s during canonicalization of %s.",
-                                 strna(n1), path);
-}
-
-int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) {
-        _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
-        _cleanup_close_ int fd = -1;
-        unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
-        bool exists = true, append_trail_slash = false;
-        struct stat previous_stat;
-        const char *todo;
-        int r;
-
-        assert(path);
-
-        /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
-        if ((flags & CHASE_NONEXISTENT) && ret_fd)
-                return -EINVAL;
-
-        if ((flags & CHASE_STEP) && ret_fd)
-                return -EINVAL;
-
-        if (isempty(path))
-                return -EINVAL;
-
-        /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
-         * symlinks relative to a root directory, instead of the root of the host.
-         *
-         * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
-         * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
-         * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
-         * prefixed accordingly.
-         *
-         * Algorithmically this operates on two path buffers: "done" are the components of the path we already
-         * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
-         * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
-         * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
-         * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
-         * to a minimum.
-         *
-         * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
-         * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
-         * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
-         * specified path.
-         *
-         * There are five ways to invoke this function:
-         *
-         * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is
-         *    returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0
-         *    is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is
-         *    returned if the destination was found, -ENOENT if it wasn't.
-         *
-         * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file
-         *    descriptor is returned as return value. This is useful to open files relative to some root
-         *    directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using
-         *    fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with
-         *    CHASE_NONEXISTENT.
-         *
-         * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first
-         *    symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if
-         *    a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the
-         *    path is fully normalized, and == 0 for each normalization step. This may be combined with
-         *    CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
-         *
-         * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
-         *    unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
-         *    CHASE_WARN is also set, a warning describing the unsafe transition is emitted.
-         *
-         * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization
-         *    is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of
-         *    the mount point is emitted.
-         */
-
-        /* A root directory of "/" or "" is identical to none */
-        if (empty_or_root(original_root))
-                original_root = NULL;
-
-        if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) {
-                /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set
-                 * and doesn't care about any of the other special features we provide either. */
-                r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
-                if (r < 0)
-                        return -errno;
-
-                *ret_fd = r;
-                return 0;
-        }
-
-        if (original_root) {
-                r = path_make_absolute_cwd(original_root, &root);
-                if (r < 0)
-                        return r;
-
-                /* Simplify the root directory, so that it has no duplicate slashes and nothing at the
-                 * end. While we won't resolve the root path we still simplify it. Note that dropping the
-                 * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY
-                 * anyway. Moreover at the end of this function after processing everything we'll always turn
-                 * the empty string back to "/". */
-                delete_trailing_chars(root, "/");
-                path_simplify(root);
-
-                if (flags & CHASE_PREFIX_ROOT) {
-                        /* We don't support relative paths in combination with a root directory */
-                        if (!path_is_absolute(path))
-                                return -EINVAL;
-
-                        path = prefix_roota(root, path);
-                }
-        }
-
-        r = path_make_absolute_cwd(path, &buffer);
-        if (r < 0)
-                return r;
-
-        fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
-        if (fd < 0)
-                return -errno;
-
-        if (flags & CHASE_SAFE)
-                if (fstat(fd, &previous_stat) < 0)
-                        return -errno;
-
-        if (flags & CHASE_TRAIL_SLASH)
-                append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/.");
-
-        if (root) {
-                /* If we are operating on a root directory, let's take the root directory as it is. */
-
-                todo = path_startswith(buffer, root);
-                if (!todo)
-                        return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG,
-                                              SYNTHETIC_ERRNO(ECHRNG),
-                                              "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
-                                              path, root);
-
-                done = strdup(root);
-        } else {
-                todo = buffer;
-                done = strdup("/");
-        }
-
-        for (;;) {
-                _cleanup_free_ char *first = NULL;
-                _cleanup_close_ int child = -1;
-                struct stat st;
-                const char *e;
-
-                r = path_find_first_component(&todo, true, &e);
-                if (r < 0)
-                        return r;
-                if (r == 0) { /* We reached the end. */
-                        if (append_trail_slash)
-                                if (!strextend(&done, "/"))
-                                        return -ENOMEM;
-                        break;
-                }
-
-                first = strndup(e, r);
-                if (!first)
-                        return -ENOMEM;
-
-                /* Two dots? Then chop off the last bit of what we already found out. */
-                if (path_equal(first, "..")) {
-                        _cleanup_free_ char *parent = NULL;
-                        _cleanup_close_ int fd_parent = -1;
-
-                        /* If we already are at the top, then going up will not change anything. This is in-line with
-                         * how the kernel handles this. */
-                        if (empty_or_root(done))
-                                continue;
-
-                        parent = dirname_malloc(done);
-                        if (!parent)
-                                return -ENOMEM;
-
-                        /* Don't allow this to leave the root dir.  */
-                        if (root &&
-                            path_startswith(done, root) &&
-                            !path_startswith(parent, root))
-                                continue;
-
-                        free_and_replace(done, parent);
-
-                        if (flags & CHASE_STEP)
-                                goto chased_one;
-
-                        fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
-                        if (fd_parent < 0)
-                                return -errno;
-
-                        if (flags & CHASE_SAFE) {
-                                if (fstat(fd_parent, &st) < 0)
-                                        return -errno;
-
-                                if (unsafe_transition(&previous_stat, &st))
-                                        return log_unsafe_transition(fd, fd_parent, path, flags);
-
-                                previous_stat = st;
-                        }
-
-                        safe_close(fd);
-                        fd = TAKE_FD(fd_parent);
-
-                        continue;
-                }
-
-                /* Otherwise let's see what this is. */
-                child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH);
-                if (child < 0) {
-                        if (errno == ENOENT &&
-                            (flags & CHASE_NONEXISTENT) &&
-                            (isempty(todo) || path_is_safe(todo))) {
-                                /* If CHASE_NONEXISTENT is set, and the path does not exist, then
-                                 * that's OK, return what we got so far. But don't allow this if the
-                                 * remaining path contains "../" or something else weird. */
-
-                                if (!path_extend(&done, first, todo))
-                                        return -ENOMEM;
-
-                                exists = false;
-                                break;
-                        }
-
-                        return -errno;
-                }
-
-                if (fstat(child, &st) < 0)
-                        return -errno;
-                if ((flags & CHASE_SAFE) &&
-                    unsafe_transition(&previous_stat, &st))
-                        return log_unsafe_transition(fd, child, path, flags);
-
-                previous_stat = st;
-
-                if ((flags & CHASE_NO_AUTOFS) &&
-                    fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
-                        return log_autofs_mount_point(child, path, flags);
-
-                if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
-                        _cleanup_free_ char *destination = NULL;
-
-                        /* This is a symlink, in this case read the destination. But let's make sure we
-                         * don't follow symlinks without bounds. */
-                        if (--max_follow <= 0)
-                                return -ELOOP;
-
-                        r = readlinkat_malloc(fd, first, &destination);
-                        if (r < 0)
-                                return r;
-                        if (isempty(destination))
-                                return -EINVAL;
-
-                        if (path_is_absolute(destination)) {
-
-                                /* An absolute destination. Start the loop from the beginning, but use the root
-                                 * directory as base. */
-
-                                safe_close(fd);
-                                fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
-                                if (fd < 0)
-                                        return -errno;
-
-                                if (flags & CHASE_SAFE) {
-                                        if (fstat(fd, &st) < 0)
-                                                return -errno;
-
-                                        if (unsafe_transition(&previous_stat, &st))
-                                                return log_unsafe_transition(child, fd, path, flags);
-
-                                        previous_stat = st;
-                                }
-
-                                /* Note that we do not revalidate the root, we take it as is. */
-                                r = free_and_strdup(&done, empty_to_root(root));
-                                if (r < 0)
-                                        return r;
-                        }
-
-                        /* Prefix what's left to do with what we just read, and start the loop again, but
-                         * remain in the current directory. */
-                        if (!path_extend(&destination, todo))
-                                return -ENOMEM;
-
-                        free_and_replace(buffer, destination);
-                        todo = buffer;
-
-                        if (flags & CHASE_STEP)
-                                goto chased_one;
-
-                        continue;
-                }
-
-                /* If this is not a symlink, then let's just add the name we read to what we already verified. */
-                if (!path_extend(&done, first))
-                        return -ENOMEM;
-
-                /* And iterate again, but go one directory further down. */
-                safe_close(fd);
-                fd = TAKE_FD(child);
-        }
-
-        if (ret_path)
-                *ret_path = TAKE_PTR(done);
-
-        if (ret_fd) {
-                /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a
-                 * proper fd by opening /proc/self/fd/xyz. */
-
-                assert(fd >= 0);
-                *ret_fd = TAKE_FD(fd);
-        }
-
-        if (flags & CHASE_STEP)
-                return 1;
-
-        return exists;
-
-chased_one:
-        if (ret_path) {
-                const char *e;
-
-                /* todo may contain slashes at the beginning. */
-                r = path_find_first_component(&todo, true, &e);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        *ret_path = TAKE_PTR(done);
-                else {
-                        char *c;
-
-                        c = path_join(done, e);
-                        if (!c)
-                                return -ENOMEM;
-
-                        *ret_path = c;
-                }
-        }
-
-        return 0;
-}
-
-int chase_symlinks_and_open(
-                const char *path,
-                const char *root,
-                unsigned chase_flags,
-                int open_flags,
-                char **ret_path) {
-
-        _cleanup_close_ int path_fd = -1;
-        _cleanup_free_ char *p = NULL;
-        int r;
-
-        if (chase_flags & CHASE_NONEXISTENT)
-                return -EINVAL;
-
-        if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
-                /* Shortcut this call if none of the special features of this call are requested */
-                r = open(path, open_flags);
-                if (r < 0)
-                        return -errno;
-
-                return r;
-        }
-
-        r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
-        if (r < 0)
-                return r;
-        assert(path_fd >= 0);
-
-        r = fd_reopen(path_fd, open_flags);
-        if (r < 0)
-                return r;
-
-        if (ret_path)
-                *ret_path = TAKE_PTR(p);
-
-        return r;
-}
-
-int chase_symlinks_and_opendir(
-                const char *path,
-                const char *root,
-                unsigned chase_flags,
-                char **ret_path,
-                DIR **ret_dir) {
-
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-        _cleanup_close_ int path_fd = -1;
-        _cleanup_free_ char *p = NULL;
-        DIR *d;
-        int r;
-
-        if (!ret_dir)
-                return -EINVAL;
-        if (chase_flags & CHASE_NONEXISTENT)
-                return -EINVAL;
-
-        if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
-                /* Shortcut this call if none of the special features of this call are requested */
-                d = opendir(path);
-                if (!d)
-                        return -errno;
-
-                *ret_dir = d;
-                return 0;
-        }
-
-        r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
-        if (r < 0)
-                return r;
-        assert(path_fd >= 0);
-
-        xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
-        d = opendir(procfs_path);
-        if (!d)
-                return -errno;
-
-        if (ret_path)
-                *ret_path = TAKE_PTR(p);
-
-        *ret_dir = d;
-        return 0;
-}
-
-int chase_symlinks_and_stat(
-                const char *path,
-                const char *root,
-                unsigned chase_flags,
-                char **ret_path,
-                struct stat *ret_stat,
-                int *ret_fd) {
-
-        _cleanup_close_ int path_fd = -1;
-        _cleanup_free_ char *p = NULL;
-        int r;
-
-        assert(path);
-        assert(ret_stat);
-
-        if (chase_flags & CHASE_NONEXISTENT)
-                return -EINVAL;
-
-        if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
-                /* Shortcut this call if none of the special features of this call are requested */
-                if (stat(path, ret_stat) < 0)
-                        return -errno;
-
-                return 1;
-        }
-
-        r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
-        if (r < 0)
-                return r;
-        assert(path_fd >= 0);
-
-        if (fstat(path_fd, ret_stat) < 0)
-                return -errno;
-
-        if (ret_path)
-                *ret_path = TAKE_PTR(p);
-        if (ret_fd)
-                *ret_fd = TAKE_FD(path_fd);
-
-        return 1;
-}
 
 int access_fd(int fd, int mode) {
-        char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-
         /* Like access() but operates on an already open fd */
 
-        xsprintf(p, "/proc/self/fd/%i", fd);
-        if (access(p, mode) < 0) {
+        if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
@@ -1385,115 +828,6 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
-int fsync_directory_of_file(int fd) {
-        _cleanup_free_ char *path = NULL;
-        _cleanup_close_ int dfd = -1;
-        struct stat st;
-        int r;
-
-        assert(fd >= 0);
-
-        /* We only reasonably can do this for regular files and directories, hence check for that */
-        if (fstat(fd, &st) < 0)
-                return -errno;
-
-        if (S_ISREG(st.st_mode)) {
-
-                r = fd_get_path(fd, &path);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
-                                        fd,
-                                        r == -ENOSYS ? ", ignoring" : "");
-
-                        if (r == -ENOSYS)
-                                /* If /proc is not available, we're most likely running in some
-                                 * chroot environment, and syncing the directory is not very
-                                 * important in that case. Let's just silently do nothing. */
-                                return 0;
-
-                        return r;
-                }
-
-                if (!path_is_absolute(path))
-                        return -EINVAL;
-
-                dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0);
-                if (dfd < 0)
-                        return dfd;
-
-        } else if (S_ISDIR(st.st_mode)) {
-                dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
-                if (dfd < 0)
-                        return -errno;
-        } else
-                return -ENOTTY;
-
-        if (fsync(dfd) < 0)
-                return -errno;
-
-        return 0;
-}
-#endif /* NM_IGNORED */
-
-int fsync_full(int fd) {
-        int r, q;
-
-        /* Sync both the file and the directory */
-
-        r = fsync(fd) < 0 ? -errno : 0;
-
-        q = fsync_directory_of_file(fd);
-        if (r < 0) /* Return earlier error */
-                return r;
-        if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a
-                           * parent dir */
-                return 0;
-        return q;
-}
-
-int fsync_path_at(int at_fd, const char *path) {
-        _cleanup_close_ int opened_fd = -1;
-        int fd;
-
-        if (isempty(path)) {
-                if (at_fd == AT_FDCWD) {
-                        opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
-                        if (opened_fd < 0)
-                                return -errno;
-
-                        fd = opened_fd;
-                } else
-                        fd = at_fd;
-        } else {
-                opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
-                if (opened_fd < 0)
-                        return -errno;
-
-                fd = opened_fd;
-        }
-
-        if (fsync(fd) < 0)
-                return -errno;
-
-        return 0;
-}
-
-int syncfs_path(int atfd, const char *path) {
-        _cleanup_close_ int fd = -1;
-
-        assert(path);
-
-        fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK);
-        if (fd < 0)
-                return -errno;
-
-        if (syncfs(fd) < 0)
-                return -errno;
-
-        return 0;
-}
-
 int open_parent(const char *path, int flags, mode_t mode) {
         _cleanup_free_ char *parent = NULL;
         int fd, r;
@@ -1516,91 +850,6 @@ int open_parent(const char *path, int flags, mode_t mode) {
 
         return fd;
 }
-
-static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
-        _cleanup_free_ char *p = NULL, *uuids = NULL;
-        _cleanup_closedir_ DIR *d = NULL;
-        int r, found_encrypted = false;
-
-        assert(sysfs_path);
-
-        if (depth_left == 0)
-                return -EINVAL;
-
-        p = path_join(sysfs_path, "dm/uuid");
-        if (!p)
-                return -ENOMEM;
-
-        r = read_one_line_file(p, &uuids);
-        if (r != -ENOENT) {
-                if (r < 0)
-                        return r;
-
-                /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
-                if (startswith(uuids, "CRYPT-"))
-                        return true;
-        }
-
-        /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
-         * subdir. */
-
-        p = mfree(p);
-        p = path_join(sysfs_path, "slaves");
-        if (!p)
-                return -ENOMEM;
-
-        d = opendir(p);
-        if (!d) {
-                if (errno == ENOENT) /* Doesn't have underlying devices */
-                        return false;
-
-                return -errno;
-        }
-
-        for (;;) {
-                _cleanup_free_ char *q = NULL;
-                struct dirent *de;
-
-                errno = 0;
-                de = readdir_no_dot(d);
-                if (!de) {
-                        if (errno != 0)
-                                return -errno;
-
-                        break; /* No more underlying devices */
-                }
-
-                q = path_join(p, de->d_name);
-                if (!q)
-                        return -ENOMEM;
-
-                r = blockdev_is_encrypted(q, depth_left - 1);
-                if (r < 0)
-                        return r;
-                if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
-                        return false;
-
-                found_encrypted = true;
-        }
-
-        return found_encrypted;
-}
-
-int path_is_encrypted(const char *path) {
-        char p[SYS_BLOCK_PATH_MAX(NULL)];
-        dev_t devt;
-        int r;
-
-        r = get_block_device(path, &devt);
-        if (r < 0)
-                return r;
-        if (r == 0) /* doesn't have a block device */
-                return false;
-
-        xsprintf_sys_block_path(p, NULL, devt);
-
-        return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
-}
 #endif /* NM_IGNORED */
 
 int conservative_renameat(
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index 7bac2570..f8a7657a 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -6,7 +6,6 @@
 #include <limits.h>
 #include <stdbool.h>
 #include <stdint.h>
-#include <sys/inotify.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -67,42 +66,6 @@ int var_tmp_dir(const char **ret);
 
 int unlink_or_warn(const char *filename);
 
-#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
-
-#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
-        for ((e) = &buffer.ev;                                \
-             (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
-             (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
-
-union inotify_event_buffer {
-        struct inotify_event ev;
-        uint8_t raw[INOTIFY_EVENT_MAX];
-};
-
-int inotify_add_watch_fd(int fd, int what, uint32_t mask);
-int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
-
-enum {
-        CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
-        CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
-        CHASE_NO_AUTOFS   = 1 << 2, /* Return -EREMOTE if autofs mount point found */
-        CHASE_SAFE        = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
-        CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
-        CHASE_STEP        = 1 << 5, /* Just execute a single step of the normalization */
-        CHASE_NOFOLLOW    = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
-                                     * right-most component refers to symlink, return O_PATH fd of the symlink. */
-        CHASE_WARN        = 1 << 7, /* Emit an appropriate warning when an error is encountered */
-};
-
-/* How many iterations to execute before returning -ELOOP */
-#define CHASE_SYMLINKS_MAX 32
-
-int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd);
-
-int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
-int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
-int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
-
 /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
 static inline char *rmdir_and_free(char *p) {
         PROTECT_ERRNO;
@@ -135,16 +98,8 @@ typedef enum UnlinkDeallocateFlags {
 
 int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
 
-int fsync_directory_of_file(int fd);
-int fsync_full(int fd);
-int fsync_path_at(int at_fd, const char *path);
-
-int syncfs_path(int atfd, const char *path);
-
 int open_parent(const char *path, int flags, mode_t mode);
 
-int path_is_encrypted(const char *path);
-
 int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
 static inline int conservative_rename(const char *oldpath, const char *newpath) {
         return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index 28db939d..ee2a12e8 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -114,7 +114,7 @@ assert_cc(IDX_FIRST == _IDX_SWAP_END);
 assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
 
 /* Storage space for the "swap" buckets.
- * All entry types can fit into a ordered_hashmap_entry. */
+ * All entry types can fit into an ordered_hashmap_entry. */
 struct swap_entries {
         struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
 };
@@ -403,7 +403,7 @@ static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_
         if (idx < _IDX_SWAP_END)
                 return &bucket_at_swap(swap, idx)->p.b;
 
-        assert_not_reached("Invalid index");
+        assert_not_reached();
 }
 
 static dib_raw_t* dib_raw_ptr(HashmapBase *h) {
@@ -515,7 +515,7 @@ static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
                 return (void*) e->key;
 
         default:
-                assert_not_reached("Unknown hashmap type");
+                assert_not_reached();
         }
 }
 
@@ -1749,7 +1749,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h  HASHMAP_DEBUG_PARAMS) {
                 r = set_merge((Set*)copy, (Set*)h);
                 break;
         default:
-                assert_not_reached("Unknown hashmap type");
+                assert_not_reached();
         }
 
         if (r < 0)
@@ -1763,6 +1763,9 @@ char** _hashmap_get_strv(HashmapBase *h) {
         Iterator i;
         unsigned idx, n;
 
+        if (!h)
+                return new0(char*, 1);
+
         sv = new(char*, n_entries(h)+1);
         if (!sv)
                 return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index f7ade2e7..eafc08f6 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -224,7 +224,7 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo
         return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
 }
 
-/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
+/* Since merging data from an OrderedHashmap into a Hashmap or vice-versa
  * should just work, allow this by having looser type-checking here. */
 int _hashmap_merge(Hashmap *h, Hashmap *other);
 #define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c
index 6eb9343f..381e79ba 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.c
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c
@@ -530,6 +530,16 @@ char base64char(int x) {
 }
 #endif /* NM_IGNORED */
 
+/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet,
+ * since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames).
+ * See section #5 of RFC 4648. */
+char urlsafe_base64char(int x) {
+        static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                      "abcdefghijklmnopqrstuvwxyz"
+                                      "0123456789-_";
+        return table[x & 63];
+}
+
 int unbase64char(char c) {
         unsigned offset;
 
@@ -560,38 +570,79 @@ int unbase64char(char c) {
 }
 
 #if 0 /* NM_IGNORED */
-ssize_t base64mem(const void *p, size_t l, char **out) {
-        char *r, *z;
+static void maybe_line_break(char **x, char *start, size_t line_break) {
+        size_t n;
+
+        assert(x);
+        assert(*x);
+        assert(start);
+        assert(*x >= start);
+
+        if (line_break == SIZE_MAX)
+                return;
+
+        n = *x - start;
+
+        if (n % (line_break + 1) == line_break)
+                *((*x)++) = '\n';
+}
+
+ssize_t base64mem_full(
+                const void *p,
+                size_t l,
+                size_t line_break,
+                char **out) {
+
         const uint8_t *x;
+        char *r, *z;
+        size_t m;
 
         assert(p || l == 0);
         assert(out);
+        assert(line_break > 0);
 
         /* three input bytes makes four output bytes, padding is added so we must round up */
-        z = r = malloc(4 * (l + 2) / 3 + 1);
+        m = 4 * (l + 2) / 3 + 1;
+
+        if (line_break != SIZE_MAX)
+                m += m / line_break;
+
+        z = r = malloc(m);
         if (!r)
                 return -ENOMEM;
 
         for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
                 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
         }
 
         switch (l % 3) {
         case 2:
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = '=';
 
                 break;
         case 1:
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = '=';
+                maybe_line_break(&z, r, line_break);
                 *(z++) = '=';
 
                 break;
@@ -599,6 +650,7 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
 
         *z = 0;
         *out = r;
+        assert(z >= r); /* Let static analyzers know that the answer is non-negative. */
         return z - r;
 }
 
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h
index 7e2a6892..5218f786 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.h
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h
@@ -27,12 +27,17 @@ char base32hexchar(int x) _const_;
 int unbase32hexchar(char c) _const_;
 
 char base64char(int x) _const_;
+char urlsafe_base64char(int x) _const_;
 int unbase64char(char c) _const_;
 
 char *base32hexmem(const void *p, size_t l, bool padding);
 int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
 
-ssize_t base64mem(const void *p, size_t l, char **out);
+ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
+static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
+        return base64mem_full(p, l, SIZE_MAX, ret);
+}
+
 int base64_append(char **prefix, int plen,
                   const void *p, size_t l,
                   int margin, int width);
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c
index 8f7679b6..3063cc7c 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.c
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.c
@@ -39,69 +39,42 @@ char* get_default_hostname(void) {
         return strdup(FALLBACK_HOSTNAME);
 }
 
-char* gethostname_malloc(void) {
+int gethostname_full(GetHostnameFlags flags, char **ret) {
+        _cleanup_free_ char *buf = NULL, *fallback = NULL;
         struct utsname u;
         const char *s;
 
-        /* This call tries to return something useful, either the actual hostname
-         * or it makes something up. The only reason it might fail is OOM.
-         * It might even return "localhost" if that's set. */
+        assert(ret);
 
         assert_se(uname(&u) >= 0);
 
         s = u.nodename;
-        if (isempty(s) || streq(s, "(none)"))
-                return get_default_hostname();
-
-        return strdup(s);
-}
-
-char* gethostname_short_malloc(void) {
-        struct utsname u;
-        const char *s;
-        _cleanup_free_ char *f = NULL;
-
-        /* Like above, but kills the FQDN part if present. */
-
-        assert_se(uname(&u) >= 0);
-
-        s = u.nodename;
-        if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
-                s = f = get_default_hostname();
+        if (isempty(s) ||
+            (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) ||
+            (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
+            (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
+                if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
+                        return -ENXIO;
+
+                s = fallback = get_default_hostname();
                 if (!s)
-                        return NULL;
+                        return -ENOMEM;
 
-                assert(s[0] != '.');
+                if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
+                        return -ENXIO;
         }
 
-        return strndup(s, strcspn(s, "."));
-}
-#endif /* NM_IGNORED */
-
-int gethostname_strict(char **ret) {
-        struct utsname u;
-        char *k;
-
-        /* This call will rather fail than make up a name. It will not return "localhost" either. */
-
-        assert_se(uname(&u) >= 0);
-
-        if (isempty(u.nodename))
-                return -ENXIO;
-
-        if (streq(u.nodename, "(none)"))
-                return -ENXIO;
-
-        if (is_localhost(u.nodename))
-                return -ENXIO;
-
-        k = strdup(u.nodename);
-        if (!k)
+        if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
+                buf = strndup(s, strcspn(s, "."));
+        else
+                buf = strdup(s);
+        if (!buf)
                 return -ENOMEM;
 
-        *ret = k;
+        *ret = TAKE_PTR(buf);
         return 0;
 }
+#endif /* NM_IGNORED */
 
 bool valid_ldh_char(char c) {
         /* "LDH" โ†’ "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
@@ -168,6 +141,7 @@ bool hostname_is_valid(const char *s, ValidHostnameFlags flags) {
         return true;
 }
 
+#if 0 /* NM_IGNORED */
 char* hostname_cleanup(char *s) {
         char *p, *d;
         bool dot, hyphen;
@@ -205,6 +179,7 @@ char* hostname_cleanup(char *s) {
 
         return s;
 }
+#endif /* NM_IGNORED */
 
 bool is_localhost(const char *hostname) {
         assert(hostname);
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h
index c3fc6752..0d1574db 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.h
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.h
@@ -4,13 +4,41 @@
 #include <stdbool.h>
 #include <stdio.h>
 
+#include "env-file.h"
 #include "macro.h"
 #include "strv.h"
 
+typedef enum GetHostnameFlags {
+        GET_HOSTNAME_ALLOW_NONE       = 1 << 0, /* accepts "(none)". */
+        GET_HOSTNAME_ALLOW_LOCALHOST  = 1 << 1, /* accepts "localhost" or friends. */
+        GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */
+        GET_HOSTNAME_SHORT            = 1 << 3, /* kills the FQDN part if present. */
+} GetHostnameFlags;
+
+int gethostname_full(GetHostnameFlags flags, char **ret);
+static inline int gethostname_strict(char **ret) {
+        return gethostname_full(0, ret);
+}
+
+static inline char* gethostname_malloc(void) {
+        char *s;
+
+        if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
+                return NULL;
+
+        return s;
+}
+
+static inline char* gethostname_short_malloc(void) {
+        char *s;
+
+        if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
+                return NULL;
+
+        return s;
+}
+
 char* get_default_hostname(void);
-char* gethostname_malloc(void);
-char* gethostname_short_malloc(void);
-int gethostname_strict(char **ret);
 
 bool valid_ldh_char(char c) _const_;
 
@@ -33,3 +61,7 @@ static inline bool is_outbound_hostname(const char *hostname) {
         /* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */
         return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
 }
+
+static inline int get_pretty_hostname(char **ret) {
+        return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret);
+}
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c
index 18c44cf1..bd4dd8fc 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -123,6 +123,12 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
         return -EAFNOSUPPORT;
 }
 
+bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) {
+        return a->s6_addr32[0] == 0 &&
+                a->s6_addr32[1] == 0 &&
+                a->s6_addr32[2] == htobe32(UINT32_C(0x0000ffff));
+}
+
 bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b) {
         assert(a);
         assert(b);
@@ -797,7 +803,7 @@ int in_addr_prefix_from_string_auto_internal(
                                 k = 0;
                         break;
                 default:
-                        assert_not_reached("Invalid prefixlen mode");
+                        assert_not_reached();
                 }
 
         if (ret_family)
@@ -834,35 +840,6 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
 
 DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
 
-static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
-        assert(a);
-        assert(state);
-
-        siphash24_compress(&a->family, sizeof(a->family), state);
-        siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
-        siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
-}
-
-static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
-        int r;
-
-        assert(x);
-        assert(y);
-
-        r = CMP(x->family, y->family);
-        if (r != 0)
-                return r;
-
-        r = CMP(x->prefixlen, y->prefixlen);
-        if (r != 0)
-                return r;
-
-        return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
-}
-
-DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free);
-
 void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
         assert(addr);
         assert(state);
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 906b3fe9..c74b0d51 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h
@@ -20,12 +20,6 @@ struct in_addr_data {
         union in_addr_union address;
 };
 
-struct in_addr_prefix {
-        int family;
-        uint8_t prefixlen;
-        union in_addr_union address;
-};
-
 bool in4_addr_is_null(const struct in_addr *a);
 static inline bool in4_addr_is_set(const struct in_addr *a) {
         return !in4_addr_is_null(a);
@@ -58,6 +52,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u);
 
 bool in4_addr_is_local_multicast(const struct in_addr *a);
 bool in4_addr_is_non_local(const struct in_addr *a);
+bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a);
 
 bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
 bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b);
@@ -123,8 +118,6 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
 int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
 
 extern const struct hash_ops in_addr_data_hash_ops;
-extern const struct hash_ops in_addr_prefix_hash_ops;
-extern const struct hash_ops in_addr_prefix_hash_ops_free;
 extern const struct hash_ops in6_addr_hash_ops;
 
 #define IPV4_ADDRESS_FMT_STR     "%u.%u.%u.%u"
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.c b/src/libnm-systemd-shared/src/basic/inotify-util.c
new file mode 100644
index 00000000..cc2a92ad
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include "fd-util.h"
+#include "inotify-util.h"
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
+        int wd;
+
+        /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
+        wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
+        if (wd < 0)
+                return -errno;
+
+        return wd;
+}
+
+int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
+        int wd;
+
+        wd = inotify_add_watch(fd, pathname, mask);
+        if (wd < 0) {
+                if (errno == ENOSPC)
+                        return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname);
+
+                return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname);
+        }
+
+        return wd;
+}
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.h b/src/libnm-systemd-shared/src/basic/inotify-util.h
new file mode 100644
index 00000000..88af0868
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <sys/inotify.h>
+
+#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
+
+#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
+        for ((e) = &buffer.ev;                                \
+             (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
+             (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
+
+union inotify_event_buffer {
+        struct inotify_event ev;
+        uint8_t raw[INOTIFY_EVENT_MAX];
+};
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask);
+int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index 256b7187..e488fff9 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -142,11 +142,8 @@
 #define LIST_FOREACH_SAFE(name,i,n,head)                                \
         for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
 
-#define LIST_FOREACH_BEFORE(name,i,p)                                   \
-        for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
-
-#define LIST_FOREACH_AFTER(name,i,p)                                    \
-        for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
+#define LIST_FOREACH_BACKWARDS(name,i,p)                                \
+        for ((i) = (p); (i); (i) = (i)->name##_prev)
 
 /* Iterate through all the members of the list p is included in, but skip over p */
 #define LIST_FOREACH_OTHERS(name,i,p)                                   \
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index 9ed01698..b11eac21 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -32,6 +32,15 @@ typedef enum LogTarget{
 #define IS_SYNTHETIC_ERRNO(val)             ((val) >> 30 & 1)
 #define ERRNO_VALUE(val)                    (abs(val) & 255)
 
+/* The callback function to be invoked when syntax warnings are seen
+ * in the unit files. */
+typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata);
+void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata);
+
+static inline void clear_log_syntax_callback(dummy_t *dummy) {
+          set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
+}
+
 const char *log_target_to_string(LogTarget target) _const_;
 LogTarget log_target_from_string(const char *s) _pure_;
 void log_set_target(LogTarget target);
@@ -273,26 +282,24 @@ _noreturn_ void log_assert_failed(
 
 #if 0 /* NM_IGNORED */
 _noreturn_ void log_assert_failed_unreachable(
-                const char *text,
                 const char *file,
                 int line,
                 const char *func);
 #else /* NM_IGNORED */
-#define log_assert_failed_unreachable(text, file, line, func)                              \
-    G_STMT_START                                                                           \
-    {                                                                                      \
-        log_internal(LOG_CRIT,                                                             \
-                     0,                                                                    \
-                     file,                                                                 \
-                     line,                                                                 \
-                     func,                                                                 \
-                     "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", \
-                     text,                                                                 \
-                     file,                                                                 \
-                     line,                                                                 \
-                     func);                                                                \
-        g_assert_not_reached();                                                            \
-    }                                                                                      \
+#define log_assert_failed_unreachable(file, line, func)                               \
+    G_STMT_START                                                                      \
+    {                                                                                 \
+        log_internal(LOG_CRIT,                                                        \
+                     0,                                                               \
+                     file,                                                            \
+                     line,                                                            \
+                     func,                                                            \
+                     "Code should not be reached at %s:%u, function %s(). Aborting.", \
+                     file,                                                            \
+                     line,                                                            \
+                     func);                                                           \
+        g_assert_not_reached();                                                       \
+    }                                                                                 \
     G_STMT_END
 #endif /* NM_IGNORED */
 
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index b0852ce5..33d28332 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -20,30 +20,14 @@
 #define _sentinel_ __attribute__((__sentinel__))
 #define _destructor_ __attribute__((__destructor__))
 #define _deprecated_ __attribute__((__deprecated__))
-#define _packed_ __attribute__((__packed__))
 #define _malloc_ __attribute__((__malloc__))
 #define _weak_ __attribute__((__weak__))
-#define _likely_(x) (__builtin_expect(!!(x), 1))
-#define _unlikely_(x) (__builtin_expect(!!(x), 0))
 #define _public_ __attribute__((__visibility__("default")))
 #define _hidden_ __attribute__((__visibility__("hidden")))
 #define _weakref_(x) __attribute__((__weakref__(#x)))
 #define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
 #define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
-#if __GNUC__ >= 7
-#define _fallthrough_ __attribute__((__fallthrough__))
-#else
-#define _fallthrough_
-#endif
-/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
- * compiler versions */
-#ifndef _noreturn_
-#if __STDC_VERSION__ >= 201112L
-#define _noreturn_ _Noreturn
-#else
-#define _noreturn_ __attribute__((__noreturn__))
-#endif
-#endif
+#define _warn_unused_result_ __attribute__((__warn_unused_result__))
 
 #if !defined(HAS_FEATURE_MEMORY_SANITIZER)
 #  if defined(__has_feature)
@@ -171,6 +155,20 @@
 #define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
 
 static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        /* Check that alignment is exponent of 2 */
+#if SIZE_MAX == UINT_MAX
+        assert(__builtin_popcount(ali) == 1);
+#elif SIZE_MAX == ULONG_MAX
+        assert(__builtin_popcountl(ali) == 1);
+#elif SIZE_MAX == ULLONG_MAX
+        assert(__builtin_popcountll(ali) == 1);
+#else
+#error "Unexpected size_t"
+#endif
+
+        if (l > SIZE_MAX - (ali - 1))
+                return SIZE_MAX; /* indicate overflow */
+
         return ((l + ali - 1) & ~(ali - 1));
 }
 
@@ -218,13 +216,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
 }
 
 /*
- * STRLEN - return the length of a string literal, minus the trailing NUL byte.
- *          Contrary to strlen(), this is a constant expression.
- * @x: a string literal.
- */
-#define STRLEN(x) (sizeof(""x"") - 1)
-
-/*
  * container_of - cast a member of a structure out to the containing structure
  * @ptr: the pointer to the member.
  * @type: the type of the container struct this is embedded in.
@@ -290,8 +281,8 @@ static inline int __coverity_check_and_return__(int condition) {
 #define assert(expr) assert_message_se(expr, #expr)
 #endif
 
-#define assert_not_reached(t)                                           \
-        log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
+#define assert_not_reached()                                            \
+        log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
 
 #define assert_return(expr, r)                                          \
         do {                                                            \
@@ -350,15 +341,15 @@ static inline int __coverity_check_and_return__(int condition) {
  * negative '-' prefix (hence works correctly on signed
  * types). Includes space for the trailing NUL. */
 #define DECIMAL_STR_MAX(type)                                           \
-        (2+(sizeof(type) <= 1 ? 3 :                                     \
-            sizeof(type) <= 2 ? 5 :                                     \
-            sizeof(type) <= 4 ? 10 :                                    \
-            sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+        (2U+(sizeof(type) <= 1 ? 3U :                                   \
+             sizeof(type) <= 2 ? 5U :                                   \
+             sizeof(type) <= 4 ? 10U :                                  \
+             sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)])))
 
 #define DECIMAL_STR_WIDTH(x)                            \
         ({                                              \
                 typeof(x) _x_ = (x);                    \
-                unsigned ans = 1;                       \
+                size_t ans = 1;                         \
                 while ((_x_ /= 10) != 0)                \
                         ans++;                          \
                 ans;                                    \
@@ -491,4 +482,10 @@ static inline size_t size_add(size_t x, size_t y) {
         return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
 }
 
+typedef struct {
+        int _empty[0];
+} dummy_t;
+
+assert_cc(sizeof(dummy_t) == 0);
+
 #include "log.h"
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.c b/src/libnm-systemd-shared/src/basic/memory-util.c
index 7ee7c94e..2d41a26a 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.c
+++ b/src/libnm-systemd-shared/src/basic/memory-util.c
@@ -20,26 +20,25 @@ size_t page_size(void) {
         return pgsz;
 }
 
-bool memeqzero(const void *data, size_t length) {
-        /* Does the buffer consist entirely of NULs?
+bool memeqbyte(uint8_t byte, const void *data, size_t length) {
+        /* Does the buffer consist entirely of the same specific byte value?
          * Copied from https://github.com/systemd/casync/, copied in turn from
          * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
          * which is licensed CC-0.
          */
 
         const uint8_t *p = data;
-        size_t i;
 
         /* Check first 16 bytes manually */
-        for (i = 0; i < 16; i++, length--) {
+        for (size_t i = 0; i < 16; i++, length--) {
                 if (length == 0)
                         return true;
-                if (p[i])
+                if (p[i] != byte)
                         return false;
         }
 
-        /* Now we know first 16 bytes are NUL, memcmp with self.  */
-        return memcmp(data, p + i, length) == 0;
+        /* Now we know first 16 bytes match, memcmp() with self.  */
+        return memcmp(data, p + 16, length) == 0;
 }
 
 #if !HAVE_EXPLICIT_BZERO
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h
index 0b04278a..9f37431f 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.h
+++ b/src/libnm-systemd-shared/src/basic/memory-util.h
@@ -47,7 +47,9 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
 
 #define zero(x) (memzero(&(x), sizeof(x)))
 
-bool memeqzero(const void *data, size_t length);
+bool memeqbyte(uint8_t byte, const void *data, size_t length);
+
+#define memeqzero(data, length) memeqbyte(0x00, data, length)
 
 #define eqzero(x) memeqzero(x, sizeof(x))
 
@@ -71,6 +73,16 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
         return memmem(haystack, haystacklen, needle, needlelen);
 }
 
+static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+        const uint8_t *p;
+
+        p = memmem_safe(haystack, haystacklen, needle, needlelen);
+        if (!p)
+                return NULL;
+
+        return (uint8_t*) p + needlelen;
+}
+
 #if HAVE_EXPLICIT_BZERO
 static inline void* explicit_bzero_safe(void *p, size_t l) {
         if (l > 0)
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index ac3cab29..aeb994f0 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -43,6 +43,26 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old)
 
 /* ======================================================================= */
 
+#if !HAVE_IOPRIO_GET
+static inline int missing_ioprio_get(int which, int who) {
+        return syscall(__NR_ioprio_get, which, who);
+}
+
+#  define ioprio_get missing_ioprio_get
+#endif
+
+/* ======================================================================= */
+
+#if !HAVE_IOPRIO_SET
+static inline int missing_ioprio_set(int which, int who, int ioprio) {
+        return syscall(__NR_ioprio_set, which, who, ioprio);
+}
+
+#  define ioprio_set missing_ioprio_set
+#endif
+
+/* ======================================================================= */
+
 #if !HAVE_MEMFD_CREATE
 static inline int missing_memfd_create(const char *name, unsigned int flags) {
 #  ifdef __NR_memfd_create
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index 13758108..f81bf1da 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -4,7 +4,6 @@
 
 #include <errno.h>
 #include <inttypes.h>
-#include <linux/oom.h>
 #include <net/if.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -738,7 +737,7 @@ int parse_oom_score_adjust(const char *s, int *ret) {
         if (r < 0)
                 return r;
 
-        if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX)
+        if (!oom_score_adjust_is_valid(v))
                 return -ERANGE;
 
         *ret = v;
@@ -748,13 +747,13 @@ int parse_oom_score_adjust(const char *s, int *ret) {
 int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) {
         assert(ret);
 
-        if (i >= (~0UL << FSHIFT))
+        if (i >= (~0UL << LOADAVG_PRECISION_BITS))
                 return -ERANGE;
 
-        i = i << FSHIFT;
-        f = DIV_ROUND_UP((f << FSHIFT), 100);
+        i = i << LOADAVG_PRECISION_BITS;
+        f = DIV_ROUND_UP((f << LOADAVG_PRECISION_BITS), 100);
 
-        if (f >= FIXED_1)
+        if (f >= LOADAVG_FIXED_POINT_1_0)
                 return -ERANGE;
 
         *ret = i | f;
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h
index 16542769..3dc5e140 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.h
+++ b/src/libnm-systemd-shared/src/basic/parse-util.h
@@ -3,9 +3,6 @@
 
 #include <inttypes.h>
 #include <limits.h>
-#if 0 /* NM_IGNORED */
-#include <linux/loadavg.h>
-#endif /* NM_IGNORED */
 #include <stddef.h>
 #include <stdint.h>
 #include <sys/types.h>
@@ -138,6 +135,14 @@ int parse_ip_prefix_length(const char *s, int *ret);
 
 int parse_oom_score_adjust(const char *s, int *ret);
 
+/* Implement floating point using fixed integers, to improve performance when
+ * calculating load averages. These macros can be used to extract the integer
+ * and decimal parts of a value. */
+#define LOADAVG_PRECISION_BITS  11
+#define LOADAVG_FIXED_POINT_1_0 (1 << LOADAVG_PRECISION_BITS)
+#define LOADAVG_INT_SIDE(x)     ((x) >> LOADAVG_PRECISION_BITS)
+#define LOADAVG_DECIMAL_SIDE(x) LOADAVG_INT_SIDE(((x) & (LOADAVG_FIXED_POINT_1_0 - 1)) * 100)
+
 /* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it
  * to a loadavg_t. */
 int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_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 55e4d317..92c55048 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -15,6 +15,7 @@
 #undef basename
 
 #include "alloc-util.h"
+#include "chase-symlinks.h"
 #include "extract-word.h"
 #include "fd-util.h"
 #include "fs-util.h"
@@ -472,8 +473,10 @@ char *path_startswith_full(const char *path, const char *prefix, bool accept_dot
 int path_compare(const char *a, const char *b) {
         int r;
 
-        assert(a);
-        assert(b);
+        /* Order NULL before non-NULL */
+        r = CMP(!!a, !!b);
+        if (r != 0)
+                return r;
 
         /* A relative path and an absolute path must not compare as equal.
          * Which one is sorted before the other does not really matter.
@@ -520,15 +523,9 @@ int path_compare(const char *a, const char *b) {
         }
 }
 
-bool path_equal(const char *a, const char *b) {
-        return path_compare(a, b) == 0;
-}
-
-#if 0 /* NM_IGNORED */
 bool path_equal_or_files_same(const char *a, const char *b, int flags) {
         return path_equal(a, b) || files_same(a, b, flags) > 0;
 }
-#endif /* NM_IGNORED */
 
 bool path_equal_filename(const char *a, const char *b) {
         _cleanup_free_ char *a_basename = NULL, *b_basename = NULL;
@@ -641,7 +638,11 @@ static int check_x_access(const char *path, int *ret_fd) {
                 return r;
 
         r = access_fd(fd, X_OK);
-        if (r < 0)
+        if (r == -ENOSYS) {
+                /* /proc is not mounted. Fallback to access(). */
+                if (access(path, X_OK) < 0)
+                        return -errno;
+        } else if (r < 0)
                 return r;
 
         if (ret_fd)
@@ -650,31 +651,54 @@ static int check_x_access(const char *path, int *ret_fd) {
         return 0;
 }
 
-int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
-        int last_error, r;
-        const char *p = NULL;
+static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
+        _cleanup_close_ int fd = -1;
+        _cleanup_free_ char *path_name = NULL;
+        int r;
 
         assert(name);
 
-        if (is_path(name)) {
-                _cleanup_close_ int fd = -1;
-
-                r = check_x_access(name, ret_fd ? &fd : NULL);
+        /* Function chase_symlinks() is invoked only when root is not NULL, as using it regardless of
+         * root value would alter the behavior of existing callers for example: /bin/sleep would become
+         * /usr/bin/sleep when find_executables is called. Hence, this function should be invoked when
+         * needed to avoid unforeseen regression or other complicated changes. */
+        if (root) {
+                r = chase_symlinks(name,
+                                   root,
+                                   CHASE_PREFIX_ROOT,
+                                   &path_name,
+                                   /* ret_fd= */ NULL); /* prefix root to name in case full paths are not specified */
                 if (r < 0)
                         return r;
 
-                if (ret_filename) {
-                        r = path_make_absolute_cwd(name, ret_filename);
-                        if (r < 0)
-                                return r;
-                }
+                name = path_name;
+        }
 
-                if (ret_fd)
-                        *ret_fd = TAKE_FD(fd);
+        r = check_x_access(name, ret_fd ? &fd : NULL);
+        if (r < 0)
+                return r;
 
-                return 0;
+        if (ret_filename) {
+                r = path_make_absolute_cwd(name, ret_filename);
+                if (r < 0)
+                        return r;
         }
 
+        if (ret_fd)
+                *ret_fd = TAKE_FD(fd);
+
+        return 0;
+}
+
+int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) {
+        int last_error = -ENOENT, r = 0;
+        const char *p = NULL;
+
+        assert(name);
+
+        if (is_path(name))
+                return find_executable_impl(name, root, ret_filename, ret_fd);
+
         if (use_path_envvar)
                 /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
                  * binary. */
@@ -682,12 +706,31 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
         if (!p)
                 p = DEFAULT_PATH;
 
-        last_error = -ENOENT;
+        if (exec_search_path) {
+                char **element;
+
+                STRV_FOREACH(element, exec_search_path) {
+                        _cleanup_free_ char *full_path = NULL;
+                        if (!path_is_absolute(*element))
+                                continue;
+                        full_path = path_join(*element, name);
+                        if (!full_path)
+                                return -ENOMEM;
+
+                        r = find_executable_impl(full_path, root, ret_filename, ret_fd);
+                        if (r < 0) {
+                                if (r != -EACCES)
+                                        last_error = r;
+                                continue;
+                        }
+                        return 0;
+                }
+                return last_error;
+        }
 
         /* Resolve a single-component name to a full path */
         for (;;) {
                 _cleanup_free_ char *element = NULL;
-                _cleanup_close_ int fd = -1;
 
                 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
                 if (r < 0)
@@ -701,7 +744,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
                 if (!path_extend(&element, name))
                         return -ENOMEM;
 
-                r = check_x_access(element, ret_fd ? &fd : NULL);
+                r = find_executable_impl(element, root, ret_filename, ret_fd);
                 if (r < 0) {
                         /* PATH entries which we don't have access to are ignored, as per tradition. */
                         if (r != -EACCES)
@@ -710,11 +753,6 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
                 }
 
                 /* Found it! */
-                if (ret_filename)
-                        *ret_filename = path_simplify(TAKE_PTR(element));
-                if (ret_fd)
-                        *ret_fd = TAKE_FD(fd);
-
                 return 0;
         }
 
@@ -722,7 +760,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
 }
 
 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
-        bool changed = false;
+        bool changed = false, originally_unset;
         const char* const* i;
 
         assert(timestamp);
@@ -730,6 +768,8 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd
         if (!paths)
                 return false;
 
+        originally_unset = *timestamp == 0;
+
         STRV_FOREACH(i, paths) {
                 struct stat stats;
                 usec_t u;
@@ -739,11 +779,11 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd
 
                 u = timespec_load(&stats.st_mtim);
 
-                /* first check */
+                /* check first */
                 if (*timestamp >= u)
                         continue;
 
-                log_debug("timestamp of '%s' changed", *i);
+                log_debug(originally_unset ? "Loaded timestamp for '%s'." : "Timestamp of '%s' changed.", *i);
 
                 /* update timestamp */
                 if (update) {
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index 8a7d5e25..fc41c869 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -64,7 +64,11 @@ static inline char* path_startswith(const char *path, const char *prefix) {
         return path_startswith_full(path, prefix, true);
 }
 int path_compare(const char *a, const char *b) _pure_;
-bool path_equal(const char *a, const char *b) _pure_;
+
+static inline bool path_equal(const char *a, const char *b) {
+        return path_compare(a, b) == 0;
+}
+
 bool path_equal_or_files_same(const char *a, const char *b, int flags);
 /* Compares only the last portion of the input paths, ie: the filenames */
 bool path_equal_filename(const char *a, const char *b);
@@ -97,9 +101,9 @@ int path_strv_make_absolute_cwd(char **l);
 char** path_strv_resolve(char **l, const char *root);
 char** path_strv_resolve_uniq(char **l, const char *root);
 
-int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd);
+int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd);
 static inline int find_executable(const char *name, char **ret_filename) {
-        return find_executable_full(name, true, ret_filename, NULL);
+        return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL);
 }
 
 bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
diff --git a/src/libnm-systemd-shared/src/basic/prioq.c b/src/libnm-systemd-shared/src/basic/prioq.c
index 19a9bc57..2f8bdf0f 100644
--- a/src/libnm-systemd-shared/src/basic/prioq.c
+++ b/src/libnm-systemd-shared/src/basic/prioq.c
@@ -175,6 +175,16 @@ int prioq_put(Prioq *q, void *data, unsigned *idx) {
         return 0;
 }
 
+int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx) {
+        int r;
+
+        r = prioq_ensure_allocated(q, compare_func);
+        if (r < 0)
+                return r;
+
+        return prioq_put(*q, data, idx);
+}
+
 static void remove_item(Prioq *q, struct prioq_item *i) {
         struct prioq_item *l;
 
diff --git a/src/libnm-systemd-shared/src/basic/prioq.h b/src/libnm-systemd-shared/src/basic/prioq.h
index 7c766476..508db880 100644
--- a/src/libnm-systemd-shared/src/basic/prioq.h
+++ b/src/libnm-systemd-shared/src/basic/prioq.h
@@ -16,6 +16,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free);
 int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
 
 int prioq_put(Prioq *q, void *data, unsigned *idx);
+int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx);
 int prioq_remove(Prioq *q, void *data, unsigned *idx);
 int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
 
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index d538dda7..96d0912c 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -31,7 +31,6 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
-#include "ioprio.h"
 #include "locale-util.h"
 #include "log.h"
 #include "macro.h"
@@ -649,20 +648,23 @@ int get_process_environ(pid_t pid, char **env) {
         return 0;
 }
 
-int get_process_ppid(pid_t pid, pid_t *_ppid) {
-        int r;
+int get_process_ppid(pid_t pid, pid_t *ret) {
         _cleanup_free_ char *line = NULL;
         long unsigned ppid;
         const char *p;
+        int r;
 
         assert(pid >= 0);
-        assert(_ppid);
 
         if (pid == 0 || pid == getpid_cached()) {
-                *_ppid = getppid();
+                if (ret)
+                        *ret = getppid();
                 return 0;
         }
 
+        if (pid == 1) /* PID 1 has no parent, shortcut this case */
+                return -EADDRNOTAVAIL;
+
         p = procfs_file_alloca(pid, "stat");
         r = read_one_line_file(p, &line);
         if (r == -ENOENT)
@@ -670,9 +672,8 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
         if (r < 0)
                 return r;
 
-        /* Let's skip the pid and comm fields. The latter is enclosed
-         * in () but does not escape any () in its value, so let's
-         * skip over it manually */
+        /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
+         * value, so let's skip over it manually */
 
         p = strrchr(line, ')');
         if (!p)
@@ -686,10 +687,17 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
                    &ppid) != 1)
                 return -EIO;
 
-        if ((long unsigned) (pid_t) ppid != ppid)
+        /* If ppid is zero the process has no parent. Which might be the case for PID 1 but also for
+         * processes originating in other namespaces that are inserted into a pidns. Return a recognizable
+         * error in this case. */
+        if (ppid == 0)
+                return -EADDRNOTAVAIL;
+
+        if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid)
                 return -ERANGE;
 
-        *_ppid = (pid_t) ppid;
+        if (ret)
+                *ret = (pid_t) ppid;
 
         return 0;
 }
@@ -826,7 +834,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
                 /* Assuming we woke due to the child exiting. */
                 if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
                         if (status.si_pid == pid) {
-                                /* This is the correct child.*/
+                                /* This is the correct child. */
                                 if (status.si_code == CLD_EXITED)
                                         return (status.si_status == 0) ? 0 : -EPROTO;
                                 else
@@ -1036,30 +1044,6 @@ bool is_main_thread(void) {
 }
 
 #if 0 /* NM_IGNORED */
-_noreturn_ void freeze(void) {
-
-        log_close();
-
-        /* Make sure nobody waits for us on a socket anymore */
-        (void) close_all_fds(NULL, 0);
-
-        sync();
-
-        /* Let's not freeze right away, but keep reaping zombies. */
-        for (;;) {
-                int r;
-                siginfo_t si = {};
-
-                r = waitid(P_ALL, 0, &si, WEXITED);
-                if (r < 0 && errno != EINTR)
-                        break;
-        }
-
-        /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
-        for (;;)
-                pause();
-}
-
 bool oom_score_adjust_is_valid(int oa) {
         return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
 }
@@ -1272,14 +1256,14 @@ static void restore_sigsetp(sigset_t **ssp) {
 
 int safe_fork_full(
                 const char *name,
-                const int except_fds[],
+                int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
                 pid_t *ret_pid) {
 
         pid_t original_pid, pid;
         sigset_t saved_ss, ss;
-        _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
+        _unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
         bool block_signals = false, block_all = false;
         int prio, r;
 
@@ -1467,7 +1451,7 @@ int safe_fork_full(
 int namespace_fork(
                 const char *outer_name,
                 const char *inner_name,
-                const int except_fds[],
+                int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
                 int pidns_fd,
@@ -1483,7 +1467,8 @@ int namespace_fork(
          * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
          * /proc/self/fd works correctly. */
 
-        r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
+        r = safe_fork_full(outer_name, except_fds, n_except_fds,
+                           (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
         if (r < 0)
                 return r;
         if (r == 0) {
@@ -1518,89 +1503,31 @@ int namespace_fork(
         return 1;
 }
 
-int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
-        bool stdout_is_tty, stderr_is_tty;
-        size_t n, i;
-        va_list ap;
-        char **l;
-        int r;
+int set_oom_score_adjust(int value) {
+        char t[DECIMAL_STR_MAX(int)];
 
-        assert(path);
+        xsprintf(t, "%i", value);
 
-        /* Spawns a temporary TTY agent, making sure it goes away when we go away */
+        return write_string_file("/proc/self/oom_score_adj", t,
+                                 WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
+}
 
-        r = safe_fork_full(name,
-                           except,
-                           n_except,
-                           FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG,
-                           ret_pid);
+int get_oom_score_adjust(int *ret) {
+        _cleanup_free_ char *t;
+        int r, a;
+
+        r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL);
         if (r < 0)
                 return r;
-        if (r > 0)
-                return 0;
-
-        /* In the child: */
 
-        stdout_is_tty = isatty(STDOUT_FILENO);
-        stderr_is_tty = isatty(STDERR_FILENO);
+        delete_trailing_chars(t, WHITESPACE);
 
-        if (!stdout_is_tty || !stderr_is_tty) {
-                int fd;
+        assert_se(safe_atoi(t, &a) >= 0);
+        assert_se(oom_score_adjust_is_valid(a));
 
-                /* Detach from stdout/stderr. and reopen
-                 * /dev/tty for them. This is important to
-                 * ensure that when systemctl is started via
-                 * popen() or a similar call that expects to
-                 * read EOF we actually do generate EOF and
-                 * not delay this indefinitely by because we
-                 * keep an unused copy of stdin around. */
-                fd = open("/dev/tty", O_WRONLY);
-                if (fd < 0) {
-                        log_error_errno(errno, "Failed to open /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
-                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
-                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                safe_close_above_stdio(fd);
-        }
-
-        (void) rlimit_nofile_safe();
-
-        /* Count arguments */
-        va_start(ap, path);
-        for (n = 0; va_arg(ap, char*); n++)
-                ;
-        va_end(ap);
-
-        /* Allocate strv */
-        l = newa(char*, n + 1);
-
-        /* Fill in arguments */
-        va_start(ap, path);
-        for (i = 0; i <= n; i++)
-                l[i] = va_arg(ap, char*);
-        va_end(ap);
-
-        execv(path, l);
-        _exit(EXIT_FAILURE);
-}
-
-int set_oom_score_adjust(int value) {
-        char t[DECIMAL_STR_MAX(int)];
-
-        sprintf(t, "%i", value);
-
-        return write_string_file("/proc/self/oom_score_adj", t,
-                                 WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
+        if (ret)
+                *ret = a;
+        return 0;
 }
 
 int pidfd_get_pid(int fd, pid_t *ret) {
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index 21e4eb91..bc16d328 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -13,8 +13,8 @@
 
 #include "alloc-util.h"
 #include "format-util.h"
-#include "ioprio.h"
 #include "macro.h"
+#include "missing_ioprio.h"
 #include "time-util.h"
 
 #define procfs_file_alloca(pid, field)                                  \
@@ -82,8 +82,6 @@ int pid_from_same_root_fs(pid_t pid);
 
 bool is_main_thread(void);
 
-_noreturn_ void freeze(void);
-
 bool oom_score_adjust_is_valid(int oa);
 
 #ifndef PERSONALITY_INVALID
@@ -170,17 +168,16 @@ typedef enum ForkFlags {
         FORK_NEW_USERNS         = 1 << 13, /* Run child in its own user namespace */
 } ForkFlags;
 
-int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
+int safe_fork_full(const char *name, int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
 
 static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
         return safe_fork_full(name, NULL, 0, flags, ret_pid);
 }
 
-int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
-
-int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_;
+int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
 
 int set_oom_score_adjust(int value);
+int get_oom_score_adjust(int *ret);
 
 /* The highest possibly (theoretic) pid_t value on this architecture. */
 #define PID_T_MAX ((pid_t) INT32_MAX)
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index 4af1fff5..063784dc 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -190,7 +190,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
                  * invocations or so. That's because we don't really care about the quality here. We
                  * generally prefer using RDRAND if the caller allows us to, since this way we won't upset
                  * the kernel's random subsystem by accessing it before the pool is initialized (after all it
-                 * will kmsg log about every attempt to do so)..*/
+                 * will kmsg log about every attempt to do so). */
                 for (;;) {
                         unsigned long u;
                         size_t m;
diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h
index 0f867393..5cae1316 100644
--- a/src/libnm-systemd-shared/src/basic/set.h
+++ b/src/libnm-systemd-shared/src/basic/set.h
@@ -26,7 +26,7 @@ static inline Set* set_free_free(Set *s) {
 
 /* no set_free_free_free */
 
-#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
+#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s)  HASHMAP_DEBUG_SRC_ARGS))
 
 int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
 #define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index a31834ab..f96eb4d4 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -241,7 +241,6 @@ int signal_from_string(const char *s) {
 void nop_signal_handler(int sig) {
         /* nothing here */
 }
-#endif /* NM_IGNORED */
 
 int signal_is_blocked(int sig) {
         sigset_t ss;
@@ -257,3 +256,39 @@ int signal_is_blocked(int sig) {
 
         return r;
 }
+
+int pop_pending_signal_internal(int sig, ...) {
+        sigset_t ss;
+        va_list ap;
+        int r;
+
+        if (sig < 0) /* Empty list? */
+                return -EINVAL;
+
+        if (sigemptyset(&ss) < 0)
+                return -errno;
+
+        /* Add first signal (if the signal is zero, we'll silently skip it, to make it easier to build
+         * parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
+         * this.) */
+        if (sig > 0 && sigaddset(&ss, sig) < 0)
+                return -errno;
+
+        /* Add all other signals */
+        va_start(ap, sig);
+        r = sigset_add_many_ap(&ss, ap);
+        va_end(ap);
+        if (r < 0)
+                return r;
+
+        r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 });
+        if (r < 0) {
+                if (errno == EAGAIN)
+                        return 0;
+
+                return -errno;
+        }
+
+        return r; /* Returns the signal popped */
+}
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.h b/src/libnm-systemd-shared/src/basic/signal-util.h
index 37271d7a..36372c19 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.h
+++ b/src/libnm-systemd-shared/src/basic/signal-util.h
@@ -62,3 +62,6 @@ static inline const char* signal_to_string_with_check(int n) {
 }
 
 int signal_is_blocked(int sig);
+
+int pop_pending_signal_internal(int sig, ...);
+#define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1)
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index da36c280..49a4df44 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -112,7 +112,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                                 /* If there's no embedded NUL byte, then the size needs to match the whole
                                  * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful,
                                  * and considers both equivalent: getsockname() even extends sockaddr_un beyond its
-                                 * size if the path is non NUL terminated.)*/
+                                 * size if the path is non NUL terminated.) */
                                 if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1))
                                         return -EINVAL;
                         }
@@ -460,23 +460,23 @@ int sockaddr_pretty(
                         if (r < 0)
                                 return -ENOMEM;
                 } else {
-                        char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1];
+                        char a[INET6_ADDRSTRLEN];
 
                         inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
-                        if (sa->in6.sin6_scope_id != 0)
-                                format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX);
 
                         if (include_port) {
-                                r = asprintf(&p,
+                                if (asprintf(&p,
                                              "[%s]:%u%s%s",
                                              a,
                                              be16toh(sa->in6.sin6_port),
                                              sa->in6.sin6_scope_id != 0 ? "%" : "",
-                                             sa->in6.sin6_scope_id != 0 ? ifname : "");
-                                if (r < 0)
+                                             FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX)) < 0)
                                         return -ENOMEM;
                         } else {
-                                p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a);
+                                if (sa->in6.sin6_scope_id != 0)
+                                        p = strjoin(a, "%", FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX));
+                                else
+                                        p = strdup(a);
                                 if (!p)
                                         return -ENOMEM;
                         }
@@ -754,6 +754,22 @@ static const char* const ip_tos_table[] = {
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
 #endif /* NM_IGNORED */
 
+bool ifname_valid_char(char a) {
+        if ((unsigned char) a >= 127U)
+                return false;
+
+        if ((unsigned char) a <= 32U)
+                return false;
+
+        if (IN_SET(a,
+                   ':',  /* colons are used by the legacy "alias" interface logic */
+                   '/',  /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */
+                   '%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */
+                return false;
+
+        return true;
+}
+
 bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
         bool numeric = true;
 
@@ -783,20 +799,11 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
 
         /* Let's refuse "all" and "default" as interface name, to avoid collisions with the special sysctl
          * directories /proc/sys/net/{ipv4,ipv6}/conf/{all,default} */
-        if (STR_IN_SET(p, "all", "default"))
+        if (!FLAGS_SET(flags, IFNAME_VALID_SPECIAL) && STR_IN_SET(p, "all", "default"))
                 return false;
 
         for (const char *t = p; *t; t++) {
-                if ((unsigned char) *t >= 127U)
-                        return false;
-
-                if ((unsigned char) *t <= 32U)
-                        return false;
-
-                if (IN_SET(*t,
-                           ':',  /* colons are used by the legacy "alias" interface logic */
-                           '/',  /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */
-                           '%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */
+                if (!ifname_valid_char(*t))
                         return false;
 
                 numeric = numeric && (*t >= '0' && *t <= '9');
@@ -921,7 +928,7 @@ int getpeergroups(int fd, gid_t **ret) {
 ssize_t send_one_fd_iov_sa(
                 int transport_fd,
                 int fd,
-                struct iovec *iov, size_t iovlen,
+                const struct iovec *iov, size_t iovlen,
                 const struct sockaddr *sa, socklen_t len,
                 int flags) {
 
@@ -929,7 +936,7 @@ ssize_t send_one_fd_iov_sa(
         struct msghdr mh = {
                 .msg_name = (struct sockaddr*) sa,
                 .msg_namelen = len,
-                .msg_iov = iov,
+                .msg_iov = (struct iovec *)iov,
                 .msg_iovlen = iovlen,
         };
         ssize_t k;
@@ -1204,7 +1211,7 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
         /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than
          * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket
          * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that
-         * do not expect non-NUL terminated file system path*/
+         * do not expect non-NUL terminated file system path. */
         if (l+1 > sizeof(ret->sun_path))
                 return -EINVAL;
 
@@ -1238,7 +1245,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
 }
 
 int socket_bind_to_ifindex(int fd, int ifindex) {
-        char ifname[IF_NAMESIZE + 1];
+        char ifname[IF_NAMESIZE];
         int r;
 
         assert(fd >= 0);
@@ -1256,8 +1263,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
                 return r;
 
         /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
-        if (!format_ifname(ifindex, ifname))
-                return -errno;
+        r = format_ifname(ifindex, ifname);
+        if (r < 0)
+                return r;
 
         return socket_bind_to_ifname(fd, ifname);
 }
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index a8a915be..4caa27c8 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -137,10 +137,12 @@ int ip_tos_to_string_alloc(int i, char **s);
 int ip_tos_from_string(const char *s);
 
 typedef enum {
-        IFNAME_VALID_ALTERNATIVE = 1 << 0,
-        IFNAME_VALID_NUMERIC     = 1 << 1,
-        _IFNAME_VALID_ALL        = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
+        IFNAME_VALID_ALTERNATIVE = 1 << 0, /* Allow "altnames" too */
+        IFNAME_VALID_NUMERIC     = 1 << 1, /* Allow decimal formatted ifindexes too */
+        IFNAME_VALID_SPECIAL     = 1 << 2, /* Allow the special names "all" and "default" */
+        _IFNAME_VALID_ALL        = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC | IFNAME_VALID_SPECIAL,
 } IfnameValidFlags;
+bool ifname_valid_char(char a);
 bool ifname_valid_full(const char *p, IfnameValidFlags flags);
 static inline bool ifname_valid(const char *p) {
         return ifname_valid_full(p, 0);
@@ -154,7 +156,7 @@ int getpeergroups(int fd, gid_t **ret);
 ssize_t send_one_fd_iov_sa(
                 int transport_fd,
                 int fd,
-                struct iovec *iov, size_t iovlen,
+                const struct iovec *iov, size_t iovlen,
                 const struct sockaddr *sa, socklen_t len,
                 int flags);
 int send_one_fd_sa(int transport_fd,
@@ -246,7 +248,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
                         _len = sizeof(struct sockaddr_vm);              \
                         break;                                          \
                 default:                                                \
-                        assert_not_reached("invalid socket family");    \
+                        assert_not_reached();                           \
                 }                                                       \
                 _len;                                                   \
         })
@@ -278,6 +280,28 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
 int socket_bind_to_ifname(int fd, const char *ifname);
 int socket_bind_to_ifindex(int fd, int ifindex);
 
+/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
+struct timeval_large {
+        uint64_t tvl_sec, tvl_usec;
+};
+struct timespec_large {
+        uint64_t tvl_sec, tvl_nsec;
+};
+
+/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
+ * See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
+ * to prevent from recvmsg_safe() returning -EXFULL. */
+#define CMSG_SPACE_TIMEVAL                                              \
+        ((sizeof(struct timeval) == sizeof(struct timeval_large)) ?     \
+         CMSG_SPACE(sizeof(struct timeval)) :                           \
+         CMSG_SPACE(sizeof(struct timeval)) +                           \
+         CMSG_SPACE(sizeof(struct timeval_large)))
+#define CMSG_SPACE_TIMESPEC                                             \
+        ((sizeof(struct timespec) == sizeof(struct timespec_large)) ?   \
+         CMSG_SPACE(sizeof(struct timespec)) :                          \
+         CMSG_SPACE(sizeof(struct timespec)) +                          \
+         CMSG_SPACE(sizeof(struct timespec_large)))
+
 ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
 
 int socket_get_family(int fd, int *ret);
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index 5112cbeb..133828bc 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -10,10 +10,11 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "chase-symlinks.h"
 #include "dirent-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
-#include "fs-util.h"
 #include "macro.h"
 #include "missing_fs.h"
 #include "missing_magic.h"
@@ -159,6 +160,7 @@ int path_is_read_only_fs(const char *path) {
 
         return false;
 }
+#endif /* NM_IGNORED */
 
 int files_same(const char *filea, const char *fileb, int flags) {
         struct stat a, b;
@@ -176,6 +178,7 @@ int files_same(const char *filea, const char *fileb, int flags) {
                a.st_ino == b.st_ino;
 }
 
+#if 0 /* NM_IGNORED */
 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
         assert(s);
         assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
diff --git a/src/libnm-systemd-shared/src/basic/stdio-util.h b/src/libnm-systemd-shared/src/basic/stdio-util.h
index d45d3c1a..f3b213c5 100644
--- a/src/libnm-systemd-shared/src/basic/stdio-util.h
+++ b/src/libnm-systemd-shared/src/basic/stdio-util.h
@@ -11,8 +11,13 @@
 #include "macro.h"
 #include "memory-util.h"
 
-#define snprintf_ok(buf, len, fmt, ...) \
-        ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
+#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")
@@ -60,7 +65,7 @@ do {                                                                    \
                         (void) va_arg(ap, long double);                 \
                         break;                                          \
                 default:                                                \
-                        assert_not_reached("Unknown format string argument."); \
+                        assert_not_reached();                           \
                 }                                                       \
         }                                                               \
 } while (false)
diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c
index c991a396..fea22ec8 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.c
+++ b/src/libnm-systemd-shared/src/basic/string-util.c
@@ -1155,4 +1155,20 @@ int string_contains_word_strv(const char *string, const char *separators, char *
                 *ret_word = found;
         return !!found;
 }
+
+bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
+        if (!s1 && !s2)
+                return true;
+        if (!s1 || !s2)
+                return false;
+
+        if (!ok)
+                ok = WHITESPACE;
+
+        for (; *s1 && *s2; s1++, s2++)
+                if (*s1 != *s2)
+                        break;
+
+        return in_charset(s1, ok) && in_charset(s2, ok);
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index 9155e50b..775d694c 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -189,22 +189,6 @@ static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
 }
 REENABLE_WARNING;
 
-/* Like startswith(), but operates on arbitrary memory blocks */
-static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
-        assert(token);
-
-        size_t n = strlen(token);
-        if (sz < n)
-                return NULL;
-
-        assert(p);
-
-        if (memcmp(p, token, n) != 0)
-                return NULL;
-
-        return (uint8_t*) p + n;
-}
-
 /* Like startswith_no_case(), but operates on arbitrary memory blocks.
  * It works only for ASCII strings.
  */
@@ -242,3 +226,5 @@ int string_contains_word_strv(const char *string, const char *separators, char *
 static inline int string_contains_word(const char *string, const char *separators, const char *word) {
         return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
 }
+
+bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index e24ef535..1d840923 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -305,6 +305,24 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
 }
 
 #if 0 /* NM_IGNORED */
+int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
+        _cleanup_strv_free_ char **l = NULL;
+        int r;
+
+        assert(t);
+        assert(s);
+
+        r = strv_split_full(&l, s, separators, flags);
+        if (r < 0)
+                return r;
+
+        r = strv_extend_strv(t, l, filter_duplicates);
+        if (r < 0)
+                return r;
+
+        return (int) strv_length(*t);
+}
+
 int strv_split_colon_pairs(char ***t, const char *s) {
         _cleanup_strv_free_ char **l = NULL;
         size_t n = 0;
@@ -831,6 +849,7 @@ char **strv_reverse(char **l) {
         return l;
 }
 
+#if 0 /* NM_IGNORED */
 char **strv_shell_escape(char **l, const char *bad) {
         char **s;
 
@@ -850,6 +869,7 @@ char **strv_shell_escape(char **l, const char *bad) {
 
         return l;
 }
+#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++)
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 911528fa..a56ef941 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -83,6 +83,9 @@ static inline char **strv_split(const char *s, const char *separators) {
         return ret;
 }
 
+int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags);
+#define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0)
+
 int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags);
 static inline char **strv_split_newlines(const char *s) {
         char **ret;
@@ -233,9 +236,11 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
 
 #define strv_free_and_replace(a, b)             \
         ({                                      \
-                strv_free(a);                   \
-                (a) = (b);                      \
-                (b) = NULL;                     \
+                char ***_a = &(a);              \
+                char ***_b = &(b);              \
+                strv_free(*_a);                 \
+                (*_a) = (*_b);                  \
+                (*_b) = NULL;                   \
                 0;                              \
         })
 
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 4b7042f7..21e7b24a 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -432,48 +432,66 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
                 s = "left";
         }
 
-        if (d >= USEC_PER_YEAR)
-                snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
-                         d / USEC_PER_YEAR,
-                         (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
-        else if (d >= USEC_PER_MONTH)
-                snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
-                         d / USEC_PER_MONTH,
-                         (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
-        else if (d >= USEC_PER_WEEK)
-                snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
-                         d / USEC_PER_WEEK,
-                         (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
-        else if (d >= 2*USEC_PER_DAY)
-                snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
+        if (d >= USEC_PER_YEAR) {
+                usec_t years = d / USEC_PER_YEAR;
+                usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
+
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                years,
+                                years == 1 ? "year" : "years",
+                                months,
+                                months == 1 ? "month" : "months",
+                                s);
+        } else if (d >= USEC_PER_MONTH) {
+                usec_t months = d / USEC_PER_MONTH;
+                usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
+
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                months,
+                                months == 1 ? "month" : "months",
+                                days,
+                                days == 1 ? "day" : "days",
+                                s);
+        } else if (d >= USEC_PER_WEEK) {
+                usec_t weeks = d / USEC_PER_WEEK;
+                usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
+
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                weeks,
+                                weeks == 1 ? "week" : "weeks",
+                                days,
+                                days == 1 ? "day" : "days",
+                                s);
+        } else if (d >= 2*USEC_PER_DAY)
+                (void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
         else if (d >= 25*USEC_PER_HOUR)
-                snprintf(buf, l, "1 day " USEC_FMT "h %s",
-                         (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
+                (void) snprintf(buf, l, "1 day " USEC_FMT "h %s",
+                                (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
         else if (d >= 6*USEC_PER_HOUR)
-                snprintf(buf, l, USEC_FMT "h %s",
-                         d / USEC_PER_HOUR, s);
+                (void) snprintf(buf, l, USEC_FMT "h %s",
+                                d / USEC_PER_HOUR, s);
         else if (d >= USEC_PER_HOUR)
-                snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
-                         d / USEC_PER_HOUR,
-                         (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
+                (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
+                                d / USEC_PER_HOUR,
+                                (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
         else if (d >= 5*USEC_PER_MINUTE)
-                snprintf(buf, l, USEC_FMT "min %s",
-                         d / USEC_PER_MINUTE, s);
+                (void) snprintf(buf, l, USEC_FMT "min %s",
+                                d / USEC_PER_MINUTE, s);
         else if (d >= USEC_PER_MINUTE)
-                snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
-                         d / USEC_PER_MINUTE,
-                         (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
+                (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
+                                d / USEC_PER_MINUTE,
+                                (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
         else if (d >= USEC_PER_SEC)
-                snprintf(buf, l, USEC_FMT "s %s",
-                         d / USEC_PER_SEC, s);
+                (void) snprintf(buf, l, USEC_FMT "s %s",
+                                d / USEC_PER_SEC, s);
         else if (d >= USEC_PER_MSEC)
-                snprintf(buf, l, USEC_FMT "ms %s",
-                         d / USEC_PER_MSEC, s);
+                (void) snprintf(buf, l, USEC_FMT "ms %s",
+                                d / USEC_PER_MSEC, s);
         else if (d > 0)
-                snprintf(buf, l, USEC_FMT"us %s",
-                         d, s);
+                (void) snprintf(buf, l, USEC_FMT"us %s",
+                                d, s);
         else
-                snprintf(buf, l, "now");
+                (void) snprintf(buf, l, "now");
 
         buf[l-1] = 0;
         return buf;
@@ -539,14 +557,12 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
 
                 /* Let's see if we should shows this in dot notation */
                 if (t < USEC_PER_MINUTE && b > 0) {
-                        usec_t cc;
-                        signed char j;
+                        signed char j = 0;
 
-                        j = 0;
-                        for (cc = table[i].usec; cc > 1; cc /= 10)
+                        for (usec_t cc = table[i].usec; cc > 1; cc /= 10)
                                 j++;
 
-                        for (cc = accuracy; cc > 1; cc /= 10) {
+                        for (usec_t cc = accuracy; cc > 1; cc /= 10) {
                                 b /= 10;
                                 j--;
                         }
@@ -1035,7 +1051,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
 
                 s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
                 if (s == p && *s != '\0')
-                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
+                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
                         return -EINVAL;
 
                 p = s;
@@ -1063,7 +1079,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
                                 r += k;
                         }
 
-                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
                         if (b == e + 1)
                                 return -EINVAL;
                 }
@@ -1210,7 +1226,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
 
                 s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
                 if (s == p && *s != '\0')
-                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
+                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
                         return -EINVAL;
 
                 p = s;
@@ -1238,7 +1254,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                                 r += k;
                         }
 
-                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
                         if (b == e + 1)
                                 return -EINVAL;
                 }
@@ -1249,75 +1265,127 @@ int parse_nsec(const char *t, nsec_t *nsec) {
         return 0;
 }
 
-int get_timezones(char ***ret) {
+static int get_timezones_from_zone1970_tab(char ***ret) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **zones = NULL;
-        size_t n_zones = 0;
         int r;
 
         assert(ret);
 
-        zones = strv_new("UTC");
-        if (!zones)
-                return -ENOMEM;
+        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
+        if (!f)
+                return -errno;
 
-        n_zones = 1;
+        for (;;) {
+                _cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
 
-        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
-        if (f) {
-                for (;;) {
-                        _cleanup_free_ char *line = NULL, *w = NULL;
-                        char *p;
-                        size_t k;
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
 
-                        r = read_line(f, LONG_LINE_MAX, &line);
-                        if (r < 0)
-                                return r;
-                        if (r == 0)
-                                break;
+                const char *p = line;
+
+                /* Line format is:
+                 * 'country codes' 'coordinates' 'timezone' 'comments' */
+                r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
+                if (r < 0)
+                        continue;
 
-                        p = strstrip(line);
+                /* Lines that start with # are comments. */
+                if (*cc == '#')
+                        continue;
+
+                r = strv_extend(&zones, tz);
+                if (r < 0)
+                        return r;
+        }
 
-                        if (isempty(p) || *p == '#')
-                                continue;
+        *ret = TAKE_PTR(zones);
+        return 0;
+}
 
-                        /* Skip over country code */
-                        p += strcspn(p, WHITESPACE);
-                        p += strspn(p, WHITESPACE);
+static int get_timezones_from_tzdata_zi(char ***ret) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_strv_free_ char **zones = NULL;
+        int r;
 
-                        /* Skip over coordinates */
-                        p += strcspn(p, WHITESPACE);
-                        p += strspn(p, WHITESPACE);
+        f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
+        if (!f)
+                return -errno;
 
-                        /* Found timezone name */
-                        k = strcspn(p, WHITESPACE);
-                        if (k <= 0)
-                                continue;
+        for (;;) {
+                _cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
 
-                        w = strndup(p, k);
-                        if (!w)
-                                return -ENOMEM;
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
 
-                        if (!GREEDY_REALLOC(zones, n_zones + 2))
-                                return -ENOMEM;
+                const char *p = line;
 
-                        zones[n_zones++] = TAKE_PTR(w);
-                        zones[n_zones] = NULL;
-                }
+                /* The only lines we care about are Zone and Link lines.
+                 * Zone line format is:
+                 * 'Zone' 'timezone' ...
+                 * Link line format is:
+                 * 'Link' 'target' 'alias'
+                 * See 'man zic' for more detail. */
+                r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL);
+                if (r < 0)
+                        continue;
 
-                strv_sort(zones);
-                strv_uniq(zones);
+                char *tz;
+                if (IN_SET(*type, 'Z', 'z'))
+                        /* Zone lines have timezone in field 1. */
+                        tz = f1;
+                else if (IN_SET(*type, 'L', 'l'))
+                        /* Link lines have timezone in field 2. */
+                        tz = f2;
+                else
+                        /* Not a line we care about. */
+                        continue;
 
-        } else if (errno != ENOENT)
-                return -errno;
+                r = strv_extend(&zones, tz);
+                if (r < 0)
+                        return r;
+        }
 
         *ret = TAKE_PTR(zones);
+        return 0;
+}
+
+int get_timezones(char ***ret) {
+        _cleanup_strv_free_ char **zones = NULL;
+        int r;
+
+        assert(ret);
+
+        r = get_timezones_from_tzdata_zi(&zones);
+        if (r == -ENOENT) {
+                log_debug_errno(r, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
+                r = get_timezones_from_zone1970_tab(&zones);
+                if (r == -ENOENT)
+                        log_debug_errno(r, "Could not get timezone data from zone1970.tab, using UTC: %m");
+        }
+        if (r < 0 && r != -ENOENT)
+                return r;
 
+        /* Always include UTC */
+        r = strv_extend(&zones, "UTC");
+        if (r < 0)
+                return -ENOMEM;
+
+        strv_sort(zones);
+        strv_uniq(zones);
+
+        *ret = TAKE_PTR(zones);
         return 0;
 }
 #endif /* NM_IGNORED */
 
-bool timezone_is_valid(const char *name, int log_level) {
+int verify_timezone(const char *name, int log_level) {
         bool slash = false;
         const char *p, *t;
         _cleanup_close_ int fd = -1;
@@ -1325,26 +1393,26 @@ bool timezone_is_valid(const char *name, int log_level) {
         int r;
 
         if (isempty(name))
-                return false;
+                return -EINVAL;
 
         /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
         if (streq(name, "UTC"))
-                return true;
+                return 0;
 
         if (name[0] == '/')
-                return false;
+                return -EINVAL;
 
         for (p = name; *p; p++) {
                 if (!(*p >= '0' && *p <= '9') &&
                     !(*p >= 'a' && *p <= 'z') &&
                     !(*p >= 'A' && *p <= 'Z') &&
                     !IN_SET(*p, '-', '_', '+', '/'))
-                        return false;
+                        return -EINVAL;
 
                 if (*p == '/') {
 
                         if (slash)
-                                return false;
+                                return -EINVAL;
 
                         slash = true;
                 } else
@@ -1352,38 +1420,31 @@ bool timezone_is_valid(const char *name, int log_level) {
         }
 
         if (slash)
-                return false;
+                return -EINVAL;
 
         if (p - name >= PATH_MAX)
-                return false;
+                return -ENAMETOOLONG;
 
         t = strjoina("/usr/share/zoneinfo/", name);
 
         fd = open(t, O_RDONLY|O_CLOEXEC);
-        if (fd < 0) {
-                log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
-                return false;
-        }
+        if (fd < 0)
+                return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
 
         r = fd_verify_regular(fd);
-        if (r < 0) {
-                log_full_errno(log_level, r, "Timezone file '%s' is not  a regular file: %m", t);
-                return false;
-        }
+        if (r < 0)
+                return log_full_errno(log_level, r, "Timezone file '%s' is not  a regular file: %m", t);
 
         r = loop_read_exact(fd, buf, 4, false);
-        if (r < 0) {
-                log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
-                return false;
-        }
+        if (r < 0)
+                return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
 
         /* Magic from tzfile(5) */
-        if (memcmp(buf, "TZif", 4) != 0) {
-                log_full(log_level, "Timezone file '%s' has wrong magic bytes", t);
-                return false;
-        }
+        if (memcmp(buf, "TZif", 4) != 0)
+                return log_full_errno(log_level, SYNTHETIC_ERRNO(EBADMSG),
+                                      "Timezone file '%s' has wrong magic bytes", t);
 
-        return true;
+        return 0;
 }
 
 bool clock_boottime_supported(void) {
@@ -1445,7 +1506,7 @@ int get_timezone(char **ret) {
 
         r = readlink_malloc("/etc/localtime", &t);
         if (r == -ENOENT) {
-                /* If the symlink does not exist, assume "UTC", like glibc does*/
+                /* If the symlink does not exist, assume "UTC", like glibc does */
                 z = strdup("UTC");
                 if (!z)
                         return -ENOMEM;
diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h
index cfde1898..895af882 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.h
+++ b/src/libnm-systemd-shared/src/basic/time-util.h
@@ -111,20 +111,31 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
 
 usec_t timespec_load(const struct timespec *ts) _pure_;
 nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
-struct timespec *timespec_store(struct timespec *ts, usec_t u);
-struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n);
+struct timespec* timespec_store(struct timespec *ts, usec_t u);
+struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
 
 usec_t timeval_load(const struct timeval *tv) _pure_;
-struct timeval *timeval_store(struct timeval *tv, usec_t u);
+struct timeval* timeval_store(struct timeval *tv, usec_t u);
 
-char *format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style);
-char *format_timestamp_relative(char *buf, size_t l, usec_t t);
-char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
+char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
+char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
+char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
 
-static inline char *format_timestamp(char *buf, size_t l, usec_t t) {
+_warn_unused_result_
+static inline char* format_timestamp(char *buf, size_t l, usec_t t) {
         return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY);
 }
 
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 ยง6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
+#define FORMAT_TIMESTAMP(t) format_timestamp((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t)
+#define FORMAT_TIMESTAMP_RELATIVE(t)                                    \
+        format_timestamp_relative((char[FORMAT_TIMESTAMP_RELATIVE_MAX]){}, FORMAT_TIMESTAMP_RELATIVE_MAX, t)
+#define FORMAT_TIMESPAN(t, accuracy) format_timespan((char[FORMAT_TIMESPAN_MAX]){}, FORMAT_TIMESPAN_MAX, t, accuracy)
+#define FORMAT_TIMESTAMP_STYLE(t, style) \
+        format_timestamp_style((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t, style)
+
 int parse_timestamp(const char *t, usec_t *usec);
 
 int parse_sec(const char *t, usec_t *usec);
@@ -134,7 +145,10 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit);
 int parse_nsec(const char *t, nsec_t *nsec);
 
 int get_timezones(char ***l);
-bool timezone_is_valid(const char *name, int log_level);
+int verify_timezone(const char *name, int log_level);
+static inline bool timezone_is_valid(const char *name, int log_level) {
+        return verify_timezone(name, log_level) >= 0;
+}
 
 bool clock_boottime_supported(void);
 bool clock_supported(clockid_t clock);
@@ -153,9 +167,8 @@ usec_t jiffies_to_usec(uint32_t jiffies);
 bool in_utc_timezone(void);
 
 static inline usec_t usec_add(usec_t a, usec_t b) {
-
-        /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't
-         * overflow. */
+        /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output,
+         * and doesn't overflow. */
 
         if (a > USEC_INFINITY - b) /* overflow check */
                 return USEC_INFINITY;
@@ -164,7 +177,6 @@ static inline usec_t usec_add(usec_t a, usec_t b) {
 }
 
 static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) {
-
         if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */
                 return USEC_INFINITY;
         if (timestamp < delta)
@@ -181,14 +193,14 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
 }
 
 #if SIZEOF_TIME_T == 8
-/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
- * territory. However, since we want to stay away from this in all timezones we take one day off. */
-#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
+  /* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
+   * year territory. However, since we want to stay away from this in all timezones we take one day off. */
+#  define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
 #elif SIZEOF_TIME_T == 4
 /* With a 32bit time_t we can't go beyond 2038... */
-#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
+#  define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
 #else
-#error "Yuck, time_t is neither 4 nor 8 bytes wide?"
+#  error "Yuck, time_t is neither 4 nor 8 bytes wide?"
 #endif
 
 int time_change_fd(void);
diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
index fd470515..8a6404d3 100644
--- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c
+++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
@@ -305,11 +305,7 @@ int link_tmpfile(int fd, const char *path, const char *target) {
                 if (r < 0)
                         return r;
         } else {
-                char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-
-                xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
-
-                if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+                if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
                         return -errno;
         }
 
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 96751860..20d8dabf 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -11,9 +11,26 @@
 #define _const_ __attribute__((__const__))
 #define _pure_ __attribute__((__pure__))
 #define _section_(x) __attribute__((__section__(x)))
+#define _packed_ __attribute__((__packed__))
 #define _used_ __attribute__((__used__))
 #define _unused_ __attribute__((__unused__))
 #define _cleanup_(x) __attribute__((__cleanup__(x)))
+#define _likely_(x) (__builtin_expect(!!(x), 1))
+#define _unlikely_(x) (__builtin_expect(!!(x), 0))
+#if __GNUC__ >= 7
+#define _fallthrough_ __attribute__((__fallthrough__))
+#else
+#define _fallthrough_
+#endif
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef _noreturn_
+#if __STDC_VERSION__ >= 201112L
+#define _noreturn_ _Noreturn
+#else
+#define _noreturn_ __attribute__((__noreturn__))
+#endif
+#endif
 
 #define XSTRINGIFY(x) #x
 #define STRINGIFY(x) XSTRINGIFY(x)
@@ -34,7 +51,14 @@
 #define CONCATENATE(x, y) XCONCATENATE(x, y)
 
 #ifdef SD_BOOT
-#define assert(expr) do {} while (false)
+        #ifdef NDEBUG
+                #define assert(expr)
+                #define assert_not_reached() __builtin_unreachable()
+        #else
+                void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
+                #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
+                #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+        #endif
 #endif
 
 #if defined(static_assert)
@@ -70,12 +94,29 @@
                 UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
         })
 
-/* evaluates to (void) if _A or _B are not constant or of different types */
+#define IS_UNSIGNED_INTEGER_TYPE(type) \
+        (__builtin_types_compatible_p(typeof(type), unsigned char) ||   \
+         __builtin_types_compatible_p(typeof(type), unsigned short) ||  \
+         __builtin_types_compatible_p(typeof(type), unsigned) ||        \
+         __builtin_types_compatible_p(typeof(type), unsigned long) ||   \
+         __builtin_types_compatible_p(typeof(type), unsigned long long))
+
+#define IS_SIGNED_INTEGER_TYPE(type) \
+        (__builtin_types_compatible_p(typeof(type), signed char) ||   \
+         __builtin_types_compatible_p(typeof(type), signed short) ||  \
+         __builtin_types_compatible_p(typeof(type), signed) ||        \
+         __builtin_types_compatible_p(typeof(type), signed long) ||   \
+         __builtin_types_compatible_p(typeof(type), signed long long))
+
+/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes
+ * is also OK as long as the signedness matches) */
 #define CONST_MAX(_A, _B) \
         (__builtin_choose_expr(                                         \
                 __builtin_constant_p(_A) &&                             \
                 __builtin_constant_p(_B) &&                             \
-                __builtin_types_compatible_p(typeof(_A), typeof(_B)),   \
+                (__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \
+                 (IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \
+                 (IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \
                 ((_A) > (_B)) ? (_A) : (_B),                            \
                 VOID_0))
 
@@ -216,3 +257,10 @@
                 (ptr) = NULL;                   \
                 _ptr_;                          \
         })
+
+/*
+ * STRLEN - return the length of a string literal, minus the trailing NUL byte.
+ *          Contrary to strlen(), this is a constant expression.
+ * @x: a string literal.
+ */
+#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
index c1abae6c..f1703926 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -79,6 +79,7 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
         return (sd_char*) s + sl - pl;
 }
 
+#if 0 /* NM_IGNORED */
 #ifdef SD_BOOT
 static sd_bool isdigit(sd_char a) {
         return a >= '0' && a <= '9';
@@ -154,7 +155,7 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
                  * Note that except for '~' prefixed segments, a string has more segments is newer.
                  * So, this check must be after the '~' check. */
                 if (*a == '\0' || *b == '\0')
-                        return strcmp(a, b);
+                        return CMP(*a, *b);
 
                 /* Handle '-', which separates version and release, e.g 123.4-3.1.fc33.x86_64 */
                 if (*a == '-' || *b == '-') {
@@ -196,9 +197,9 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
 
                         /* Find the leading numeric segments. One may be an empty string. So,
                          * numeric segments are always newer than alpha segments. */
-                        for (aa = a; *aa != '\0' && isdigit(*aa); aa++)
+                        for (aa = a; isdigit(*aa); aa++)
                                 ;
-                        for (bb = b; *bb != '\0' && isdigit(*bb); bb++)
+                        for (bb = b; isdigit(*bb); bb++)
                                 ;
 
                         /* To compare numeric segments without parsing their values, first compare the
@@ -213,9 +214,9 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
                                 return r;
                 } else {
                         /* Find the leading non-numeric segments. */
-                        for (aa = a; *aa != '\0' && is_alpha(*aa); aa++)
+                        for (aa = a; is_alpha(*aa); aa++)
                                 ;
-                        for (bb = b; *bb != '\0' && is_alpha(*bb); bb++)
+                        for (bb = b; is_alpha(*bb); bb++)
                                 ;
 
                         /* Note that the segments are usually not NUL-terminated. */
@@ -234,3 +235,4 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
                 b = bb;
         }
 }
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
index 407cede4..7455c054 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -16,6 +16,7 @@
 #define strncmp(a, b, n) StrnCmp((a), (b), (n))
 #define strcasecmp(a, b) StriCmp((a), (b))
 #define STR_C(str)       (L ## str)
+#define memcmp(a, b, n)  CompareMem(a, b, n)
 #else
 #define STR_C(str)       (str)
 #endif
@@ -65,3 +66,19 @@ static inline const sd_char *yes_no(sd_bool b) {
 }
 
 sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
+
+/* Like startswith(), but operates on arbitrary memory blocks */
+static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) {
+        assert(token);
+
+        sd_size_t n = strlen(token) * sizeof(sd_char);
+        if (sz < n)
+                return NULL;
+
+        assert(p);
+
+        if (memcmp(p, token, n) != 0)
+                return NULL;
+
+        return (uint8_t*) p + n;
+}
diff --git a/src/libnm-systemd-shared/src/shared/log-link.h b/src/libnm-systemd-shared/src/shared/log-link.h
index 51eaa0c0..5f2b1763 100644
--- a/src/libnm-systemd-shared/src/shared/log-link.h
+++ b/src/libnm-systemd-shared/src/shared/log-link.h
@@ -17,24 +17,6 @@
                 log_interface_full_errno_zerook(ifname, level, _error, __VA_ARGS__); \
         })
 
-#define log_interface_prefix_full_errno_zerook(prefix, ifname_expr, error, fmt, ...) \
-        ({                                                              \
-                int _e = (error);                                       \
-                if (DEBUG_LOGGING)                                      \
-                        log_interface_full_errno_zerook(                \
-                                ifname_expr,                            \
-                                LOG_DEBUG, _e, prefix fmt,              \
-                                ##__VA_ARGS__);                         \
-                -ERRNO_VALUE(_e);                                       \
-        })
-
-#define log_interface_prefix_full_errno(prefix, ifname_expr, error, fmt, ...) \
-        ({                                                              \
-                int _error = (error);                                   \
-                ASSERT_NON_ZERO(_error);                                \
-                log_interface_prefix_full_errno_zerook(prefix, ifname_expr, _error, fmt, ##__VA_ARGS__); \
-        })
-
 /*
  * The following macros append INTERFACE= to the message.
  * The macros require a struct named 'Link' which contains 'char *ifname':
diff --git a/src/libnm-systemd-shared/src/shared/web-util.c b/src/libnm-systemd-shared/src/shared/web-util.c
index 35ba1a2e..85532bdf 100644
--- a/src/libnm-systemd-shared/src/shared/web-util.c
+++ b/src/libnm-systemd-shared/src/shared/web-util.c
@@ -45,16 +45,29 @@ bool http_etag_is_valid(const char *etag) {
 /**/   }
 
 #if 0 /* NM_IGNORED */
+bool file_url_is_valid(const char *url) {
+        const char *p;
+
+        if (isempty(url))
+                return false;
+
+        p = startswith(url, "file:/");
+        if (isempty(p))
+                return false;
+
+        return ascii_is_valid(p);
+}
+
 bool documentation_url_is_valid(const char *url) {
         const char *p;
 
         if (isempty(url))
                 return false;
 
-        if (http_url_is_valid(url))
+        if (http_url_is_valid(url) || file_url_is_valid(url))
                 return true;
 
-        p = STARTSWITH_SET(url, "file:/", "info:", "man:");
+        p = STARTSWITH_SET(url, "info:", "man:");
         if (isempty(p))
                 return false;
 
diff --git a/src/libnm-systemd-shared/src/shared/web-util.h b/src/libnm-systemd-shared/src/shared/web-util.h
index ec54669f..88b4897b 100644
--- a/src/libnm-systemd-shared/src/shared/web-util.h
+++ b/src/libnm-systemd-shared/src/shared/web-util.h
@@ -6,6 +6,7 @@
 #include "macro.h"
 
 bool http_url_is_valid(const char *url) _pure_;
+bool file_url_is_valid(const char *url) _pure_;
 
 bool documentation_url_is_valid(const char *url) _pure_;