From f1d7bf81575efca716a09c7a7707e538ca43d7e2 Mon Sep 17 00:00:00 2001
From: Michael Biebl <biebl@debian.org>
Date: Sun, 20 Mar 2022 20:55:25 +0100
Subject: [PATCH] Import Debian changes 247.3-7

---
 debian/changelog                              |  30 ++
 debian/control                                |   6 +-
 debian/gbp.conf                               |   2 +-
 ...ic-add-make_mount_point_inode-helper.patch | 239 +++++++++
 .../basic-unit-name-adjust-comments.patch     |  12 +-
 ...it-name-do-not-use-strdupa-on-a-path.patch |  13 +-
 ...-that-abstracts-might-be-btrfs-subvo.patch | 106 ++++
 .../debian/Revert-udev-fix-memleak.patch      |  30 ++
 ...te-should-fail-if-the-entry-in-symli.patch |  47 ++
 ...rithm-that-selects-highest-priority-.patch | 163 ++++++
 ...message-to-use-normalized-instead-of.patch |  28 +
 ...out-helper-function-to-add-airlocked.patch | 499 ++++++++++++++++++
 ...PID-namespace-when-adding-a-live-mou.patch | 105 ++++
 ...ail-if-containing-dir-has-limited-ac.patch | 128 +++++
 ...-fsync-after-removing-directory-tree.patch |  39 ++
 ...children-split-out-body-of-directory.patch | 320 +++++++++++
 debian/patches/series                         |  17 +
 ...-mount-util-use-namespace_fork-utils.patch |  92 ++++
 ...r-nested-directories-instead-of-inst.patch | 264 +++++++++
 ...refactor-rm_rf-to-shorten-code-a-bit.patch |  99 ++++
 ...-rm_rf_children_inner-to-shorten-cod.patch |  66 +++
 ...-st-may-have-been-used-uninitialized.patch |  27 +
 ...-do-not-return-immediately-on-EACCES.patch |  58 ++
 ...te-a-clear-error-code-when-convertin.patch |  22 +-
 debian/tests/control                          |   1 +
 25 files changed, 2387 insertions(+), 26 deletions(-)
 create mode 100644 debian/patches/basic-add-make_mount_point_inode-helper.patch
 create mode 100644 debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch
 create mode 100644 debian/patches/debian/Revert-udev-fix-memleak.patch
 create mode 100644 debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch
 create mode 100644 debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch
 create mode 100644 debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch
 create mode 100644 debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch
 create mode 100644 debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch
 create mode 100644 debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch
 create mode 100644 debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch
 create mode 100644 debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch
 create mode 100644 debian/patches/shared-mount-util-use-namespace_fork-utils.patch
 create mode 100644 debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch
 create mode 100644 debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch
 create mode 100644 debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch
 create mode 100644 debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch
 create mode 100644 debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch

diff --git a/debian/changelog b/debian/changelog
index 4659f3b2..ddb37016 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,33 @@
+systemd (247.3-7) bullseye; urgency=medium
+
+  * Switch debian-branch to debian/bullseye
+  * udevadm-trigger: do not return immediately on EACCES.
+    Fixes a regression when using systemd-networkd in an unprivileged LXD
+    container. (Closes: #997006)
+  * Revert multipath symlink race fix.
+    Revert upstream commits which caused a regression in udev resulting in
+    long delays when processing partitions with the same label.
+    (Closes: #993738)
+  * shared/rm-rf: loop over nested directories instead of recursing.
+    Fixes uncontrolled recursion in systemd-tmpfiles.
+    (CVE-2021-3997, Closes: #1003467)
+  * Demote systemd-timesyncd from Depends to Recommends.
+    This avoids a dependency cycle between systemd and systemd-timesyncd and
+    thus makes dist upgrades more predictable and robust.
+    It also allows minimal, systemd based containers where no NTP client is
+    strictly necessary.
+    To ensure that systemd-timesyncd is installed in a default installation
+    created by d-i, bump its priority to standard.
+    (Closes: #986651, #993947)
+  * autopktest: Fix timedated test dependencies.
+    Add an explicit systemd-timesyncd dependency as it is required by the
+    timedated test.
+  * machine: enter target PID namespace when adding a live mount.
+    Fixes failure to bind mount a directory into a container using
+    machinectl. (Closes: #993248)
+
+ -- Michael Biebl <biebl@debian.org>  Sun, 20 Mar 2022 20:55:25 +0100
+
 systemd (247.3-6) unstable; urgency=high
 
   * Non-maintainer upload (acked by maintainers)
diff --git a/debian/control b/debian/control
index be7c47b9..c0cc0dc3 100644
--- a/debian/control
+++ b/debian/control
@@ -65,7 +65,8 @@ Architecture: linux-any
 Multi-Arch: foreign
 Section: admin
 Priority: important
-Recommends: dbus
+Recommends: dbus,
+            systemd-timesyncd | time-daemon,
 Suggests: systemd-container,
           policykit-1
 Pre-Depends: ${shlibs:Pre-Depends},
@@ -73,7 +74,6 @@ Pre-Depends: ${shlibs:Pre-Depends},
 Depends: ${shlibs:Depends},
          ${misc:Depends},
          libsystemd0 (= ${binary:Version}),
-         systemd-timesyncd | time-daemon,
          util-linux (>= 2.27.1),
          mount (>= 2.26),
          adduser,
@@ -185,7 +185,7 @@ Package: systemd-timesyncd
 Architecture: linux-any
 Multi-Arch: foreign
 Section: admin
-Priority: optional
+Priority: standard
 Depends: ${shlibs:Depends},
          ${misc:Depends},
          adduser,
diff --git a/debian/gbp.conf b/debian/gbp.conf
index fb40ad37..a34c5970 100644
--- a/debian/gbp.conf
+++ b/debian/gbp.conf
@@ -1,7 +1,7 @@
 [DEFAULT]
 pristine-tar = True
 patch-numbers = False
-debian-branch = debian/master
+debian-branch = debian/bullseye
 upstream-branch = upstream/latest
 
 [dch]
diff --git a/debian/patches/basic-add-make_mount_point_inode-helper.patch b/debian/patches/basic-add-make_mount_point_inode-helper.patch
new file mode 100644
index 00000000..49207c39
--- /dev/null
+++ b/debian/patches/basic-add-make_mount_point_inode-helper.patch
@@ -0,0 +1,239 @@
+From: Luca Boccassi <bluca@debian.org>
+Date: Sat, 19 Dec 2020 21:40:47 +0000
+Subject: basic: add make_mount_point_inode helper
+
+Creates a file or a directory depending on the source path, useful
+for creating mount points.
+
+(cherry picked from commit 8bab8029105e44ce78c5e11bffa203a1135fe201)
+---
+ src/basic/mountpoint-util.c     | 25 +++++++++++++++++++++
+ src/basic/mountpoint-util.h     |  4 ++++
+ src/core/namespace.c            | 26 +++++++--------------
+ src/machine/machine-dbus.c      | 14 ++++--------
+ src/test/test-mountpoint-util.c | 50 +++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 91 insertions(+), 28 deletions(-)
+
+diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c
+index a6602ad..ed7457f 100644
+--- a/src/basic/mountpoint-util.c
++++ b/src/basic/mountpoint-util.c
+@@ -8,14 +8,17 @@
+ #include "fd-util.h"
+ #include "fileio.h"
+ #include "fs-util.h"
++#include "label.h"
+ #include "missing_stat.h"
+ #include "missing_syscall.h"
++#include "mkdir.h"
+ #include "mountpoint-util.h"
+ #include "parse-util.h"
+ #include "path-util.h"
+ #include "stat-util.h"
+ #include "stdio-util.h"
+ #include "strv.h"
++#include "user-util.h"
+ 
+ /* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
+  * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
+@@ -509,3 +512,25 @@ int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
+                 return -EINVAL;
+         return 0;
+ }
++
++int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode) {
++        assert(st);
++        assert(dest);
++
++        if (S_ISDIR(st->st_mode))
++                return mkdir_label(dest, mode);
++        else
++                return mknod(dest, S_IFREG|(mode & ~0111), 0);
++}
++
++int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode) {
++        struct stat st;
++
++        assert(source);
++        assert(dest);
++
++        if (stat(source, &st) < 0)
++                return -errno;
++
++        return make_mount_point_inode_from_stat(&st, dest, mode);
++}
+diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h
+index aadb212..cebcec5 100644
+--- a/src/basic/mountpoint-util.h
++++ b/src/basic/mountpoint-util.h
+@@ -23,3 +23,7 @@ int dev_is_devtmpfs(void);
+ 
+ const char *mount_propagation_flags_to_string(unsigned long flags);
+ int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
++
++/* Creates a mount point (not parents) based on the source path or stat - ie, a file or a directory */
++int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode);
++int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode);
+diff --git a/src/core/namespace.c b/src/core/namespace.c
+index cdf427a..02381da 100644
+--- a/src/core/namespace.c
++++ b/src/core/namespace.c
+@@ -1176,29 +1176,19 @@ static int apply_mount(
+                 bool try_again = false;
+ 
+                 if (r == -ENOENT && make) {
+-                        struct stat st;
++                        int q;
+ 
+                         /* Hmm, either the source or the destination are missing. Let's see if we can create
+                            the destination, then try again. */
+ 
+-                        if (stat(what, &st) < 0)
+-                                log_error_errno(errno, "Mount point source '%s' is not accessible: %m", what);
+-                        else {
+-                                int q;
++                        (void) mkdir_parents(mount_entry_path(m), 0755);
+ 
+-                                (void) mkdir_parents(mount_entry_path(m), 0755);
+-
+-                                if (S_ISDIR(st.st_mode))
+-                                        q = mkdir(mount_entry_path(m), 0755) < 0 ? -errno : 0;
+-                                else
+-                                        q = touch(mount_entry_path(m));
+-
+-                                if (q < 0)
+-                                        log_error_errno(q, "Failed to create destination mount point node '%s': %m",
+-                                                        mount_entry_path(m));
+-                                else
+-                                        try_again = true;
+-                        }
++                        q = make_mount_point_inode_from_path(what, mount_entry_path(m), 0755);
++                        if (q < 0)
++                                log_error_errno(q, "Failed to create destination mount point node '%s': %m",
++                                                mount_entry_path(m));
++                        else
++                                try_again = true;
+                 }
+ 
+                 if (try_again)
+diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
+index bb67beb..1105008 100644
+--- a/src/machine/machine-dbus.c
++++ b/src/machine/machine-dbus.c
+@@ -32,6 +32,7 @@
+ #include "missing_capability.h"
+ #include "mkdir.h"
+ #include "mount-util.h"
++#include "mountpoint-util.h"
+ #include "namespace-util.h"
+ #include "os-util.h"
+ #include "path-util.h"
+@@ -908,10 +909,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
+ 
+         /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
+         mount_tmp = strjoina(mount_slave, "/mount");
+-        if (S_ISDIR(st.st_mode))
+-                r = mkdir_errno_wrapper(mount_tmp, 0700);
+-        else
+-                r = touch(mount_tmp);
++        r = make_mount_point_inode_from_stat(&st, mount_tmp, 0700);
+         if (r < 0) {
+                 sd_bus_error_set_errnof(error, r, "Failed to create temporary mount point %s: %m", mount_tmp);
+                 goto finish;
+@@ -1003,12 +1001,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
+                 }
+ 
+                 if (make_file_or_directory) {
+-                        if (S_ISDIR(st.st_mode))
+-                                (void) mkdir_p(dest, 0755);
+-                        else {
+-                                (void) mkdir_parents(dest, 0755);
+-                                (void) mknod(dest, S_IFREG|0600, 0);
+-                        }
++                        (void) mkdir_parents(dest, 0755);
++                        (void) make_mount_point_inode_from_stat(&st, dest, 0700);
+                 }
+ 
+                 mount_inside = strjoina("/run/host/incoming/", basename(mount_outside));
+diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c
+index 47fde5c..1ce3d5d 100644
+--- a/src/test/test-mountpoint-util.c
++++ b/src/test/test-mountpoint-util.c
+@@ -8,13 +8,16 @@
+ #include "def.h"
+ #include "fd-util.h"
+ #include "fileio.h"
++#include "fs-util.h"
+ #include "hashmap.h"
+ #include "log.h"
++#include "mkdir.h"
+ #include "mountpoint-util.h"
+ #include "path-util.h"
+ #include "rm-rf.h"
+ #include "string-util.h"
+ #include "tests.h"
++#include "tmpfile-util.h"
+ 
+ static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
+         long unsigned flags;
+@@ -287,6 +290,52 @@ static void test_fd_is_mount_point(void) {
+         assert_se(IN_SET(fd_is_mount_point(fd, "root/", 0), -ENOENT, 0));
+ }
+ 
++static void test_make_mount_point_inode(void) {
++        _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
++        const char *src_file, *src_dir, *dst_file, *dst_dir;
++        struct stat st;
++
++        log_info("/* %s */", __func__);
++
++        assert_se(mkdtemp_malloc(NULL, &d) >= 0);
++
++        src_file = strjoina(d, "/src/file");
++        src_dir = strjoina(d, "/src/dir");
++        dst_file = strjoina(d, "/dst/file");
++        dst_dir = strjoina(d, "/dst/dir");
++
++        assert_se(mkdir_p(src_dir, 0755) >= 0);
++        assert_se(mkdir_parents(dst_file, 0755) >= 0);
++        assert_se(touch(src_file) >= 0);
++
++        assert_se(make_mount_point_inode_from_path(src_file, dst_file, 0755) >= 0);
++        assert_se(make_mount_point_inode_from_path(src_dir, dst_dir, 0755) >= 0);
++
++        assert_se(stat(dst_dir, &st) == 0);
++        assert_se(S_ISDIR(st.st_mode));
++        assert_se(stat(dst_file, &st) == 0);
++        assert_se(S_ISREG(st.st_mode));
++        assert_se(!(S_IXUSR & st.st_mode));
++        assert_se(!(S_IXGRP & st.st_mode));
++        assert_se(!(S_IXOTH & st.st_mode));
++
++        assert_se(unlink(dst_file) == 0);
++        assert_se(rmdir(dst_dir) == 0);
++
++        assert_se(stat(src_file, &st) == 0);
++        assert_se(make_mount_point_inode_from_stat(&st, dst_file, 0755) >= 0);
++        assert_se(stat(src_dir, &st) == 0);
++        assert_se(make_mount_point_inode_from_stat(&st, dst_dir, 0755) >= 0);
++
++        assert_se(stat(dst_dir, &st) == 0);
++        assert_se(S_ISDIR(st.st_mode));
++        assert_se(stat(dst_file, &st) == 0);
++        assert_se(S_ISREG(st.st_mode));
++        assert_se(!(S_IXUSR & st.st_mode));
++        assert_se(!(S_IXGRP & st.st_mode));
++        assert_se(!(S_IXOTH & st.st_mode));
++}
++
+ int main(int argc, char *argv[]) {
+         test_setup_logging(LOG_DEBUG);
+ 
+@@ -311,6 +360,7 @@ int main(int argc, char *argv[]) {
+         test_mnt_id();
+         test_path_is_mount_point();
+         test_fd_is_mount_point();
++        test_make_mount_point_inode();
+ 
+         return 0;
+ }
diff --git a/debian/patches/basic-unit-name-adjust-comments.patch b/debian/patches/basic-unit-name-adjust-comments.patch
index d46e0c9f..d83b1d7f 100644
--- a/debian/patches/basic-unit-name-adjust-comments.patch
+++ b/debian/patches/basic-unit-name-adjust-comments.patch
@@ -1,18 +1,19 @@
-From cbcea9f517bfe79b019fcec5c364952ea33d24f2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
 Date: Wed, 23 Jun 2021 11:52:56 +0200
 Subject: basic/unit-name: adjust comments
 MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
+Content-Type: text/plain; charset="utf-8"
 Content-Transfer-Encoding: 8bit
 
 We already checked for "too long" right above…
+
+(cherry picked from commit 4e2544c30bfb95e7cb4d1551ba066b1a56520ad6)
 ---
  src/basic/unit-name.c | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
 
 diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
-index a22763443fdd..1deead74588b 100644
+index 9b6cacd..e286831 100644
 --- a/src/basic/unit-name.c
 +++ b/src/basic/unit-name.c
 @@ -528,7 +528,7 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) {
@@ -33,6 +34,3 @@ index a22763443fdd..1deead74588b 100644
          if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE))
                  return -EINVAL;
  
--- 
-2.32.0
-
diff --git a/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch b/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch
index 0faa7d19..b080d255 100644
--- a/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch
+++ b/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch
@@ -1,5 +1,4 @@
-From bae2f0d1109a8c75a7fb89ae6b8d1b6ef8dfab16 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
 Date: Wed, 23 Jun 2021 11:46:41 +0200
 Subject: basic/unit-name: do not use strdupa() on a path
 
@@ -19,12 +18,17 @@ simplification, which in turns uses a copy of the string we can write to.
 So we can't reject paths that are too long before doing the duplication.
 Hence the most obvious solution is to switch back to strdup(), as before
 7410616cd9dbbec97cf98d75324da5cda2b2f7a2.
+
+(cherry picked from commit 441e0115646d54f080e5c3bb0ba477c892861ab9)
+(cherry picked from commit 764b74113e36ac5219a4b82a05f311b5a92136ce)
+(cherry picked from commit 4a1c5f34bd3e1daed4490e9d97918e504d19733b)
+(cherry picked from commit b00674347337b7531c92fdb65590ab253bb57538)
 ---
  src/basic/unit-name.c | 13 +++++--------
  1 file changed, 5 insertions(+), 8 deletions(-)
 
 diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
-index 284a77348316..a22763443fdd 100644
+index 5f595af..9b6cacd 100644
 --- a/src/basic/unit-name.c
 +++ b/src/basic/unit-name.c
 @@ -378,12 +378,13 @@ int unit_name_unescape(const char *f, char **ret) {
@@ -59,6 +63,3 @@ index 284a77348316..a22763443fdd 100644
          }
          if (!s)
                  return -ENOMEM;
--- 
-2.32.0
-
diff --git a/debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch b/debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch
new file mode 100644
index 00000000..0dffcf30
--- /dev/null
+++ b/debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch
@@ -0,0 +1,106 @@
+From: Lennart Poettering <lennart@poettering.net>
+Date: Fri, 26 Feb 2021 17:39:55 +0100
+Subject: btrfs-util: add helper that abstracts "might be btrfs subvol?" check
+
+Let#s not hardcode inode nr 256 everywhere, but abstract this check
+slightly.
+
+(cherry picked from commit 674b04ff1b6deab17f5d36c036c0275ba94e1ebc)
+(cherry picked from commit 190c6bcfc3518bec964ab740085ac88ccc86dcc7)
+---
+ src/basic/btrfs-util.c     |  6 +++---
+ src/basic/btrfs-util.h     | 10 ++++++++++
+ src/basic/rm-rf.c          |  2 +-
+ src/import/export-tar.c    |  2 +-
+ src/shared/machine-image.c |  3 +--
+ 5 files changed, 16 insertions(+), 7 deletions(-)
+
+diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
+index 2634659..f0df51a 100644
+--- a/src/basic/btrfs-util.c
++++ b/src/basic/btrfs-util.c
+@@ -91,7 +91,7 @@ int btrfs_is_subvol_fd(int fd) {
+         if (fstat(fd, &st) < 0)
+                 return -errno;
+ 
+-        if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
++        if (!btrfs_might_be_subvol(&st))
+                 return 0;
+ 
+         return btrfs_is_filesystem(fd);
+@@ -194,7 +194,7 @@ int btrfs_subvol_set_read_only_fd(int fd, bool b) {
+         if (fstat(fd, &st) < 0)
+                 return -errno;
+ 
+-        if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
++        if (!btrfs_might_be_subvol(&st))
+                 return -EINVAL;
+ 
+         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
+@@ -229,7 +229,7 @@ int btrfs_subvol_get_read_only_fd(int fd) {
+         if (fstat(fd, &st) < 0)
+                 return -errno;
+ 
+-        if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
++        if (!btrfs_might_be_subvol(&st))
+                 return -EINVAL;
+ 
+         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
+diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
+index c8b44f6..0f569b6 100644
+--- a/src/basic/btrfs-util.h
++++ b/src/basic/btrfs-util.h
+@@ -127,3 +127,13 @@ static inline int btrfs_log_dev_root(int level, int ret, const char *p) {
+                               "File system behind %s is reported by btrfs to be backed by pseudo-device /dev/root, which is not a valid userspace accessible device node. "
+                               "Cannot determine correct backing block device.", p);
+ }
++
++static inline bool btrfs_might_be_subvol(const struct stat *st) {
++        if (!st)
++                return false;
++
++        /* Returns true if this 'struct stat' looks like it could refer to a btrfs subvolume. To make a final
++         * decision, needs to be combined with an fstatfs() check to see if this is actually btrfs. */
++
++        return S_ISDIR(st->st_mode) && st->st_ino == 256;
++}
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index b0d682f..4c39ce8 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -147,7 +147,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
+                         if (r > 0)
+                                 continue;
+ 
+-                        if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
++                        if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
+ 
+                                 /* This could be a subvolume, try to remove it */
+ 
+diff --git a/src/import/export-tar.c b/src/import/export-tar.c
+index b8b650f..1e6b2c1 100644
+--- a/src/import/export-tar.c
++++ b/src/import/export-tar.c
+@@ -283,7 +283,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
+ 
+         e->quota_referenced = (uint64_t) -1;
+ 
+-        if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */
++        if (btrfs_might_be_subvol(&e->st)) {
+                 BtrfsQuotaInfo q;
+ 
+                 r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q);
+diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
+index 671a56b..c7cf5e9 100644
+--- a/src/shared/machine-image.c
++++ b/src/shared/machine-image.c
+@@ -248,8 +248,7 @@ static int image_make(
+                 if (fd < 0)
+                         return -errno;
+ 
+-                /* btrfs subvolumes have inode 256 */
+-                if (st->st_ino == 256) {
++                if (btrfs_might_be_subvol(st)) {
+ 
+                         r = btrfs_is_filesystem(fd);
+                         if (r < 0)
diff --git a/debian/patches/debian/Revert-udev-fix-memleak.patch b/debian/patches/debian/Revert-udev-fix-memleak.patch
new file mode 100644
index 00000000..ed90a109
--- /dev/null
+++ b/debian/patches/debian/Revert-udev-fix-memleak.patch
@@ -0,0 +1,30 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Sat, 25 Sep 2021 21:07:17 +0200
+Subject: Revert "udev: fix memleak"
+
+This reverts commit 5dd2b56443e2ed81c238094f516a622804b35518.
+---
+ src/udev/udev-node.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
+index b8b93ee..2cc78c9 100644
+--- a/src/udev/udev-node.c
++++ b/src/udev/udev-node.c
+@@ -194,7 +194,7 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
+ 
+ /* manage "stack of names" with possibly specified device priorities */
+ static int link_update(sd_device *dev, const char *slink, bool add) {
+-        _cleanup_free_ char *filename = NULL, *dirname = NULL;
++        _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL;
+         char name_enc[PATH_MAX];
+         const char *id_filename;
+         int i, r, retries;
+@@ -237,7 +237,6 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
+         retries = sd_device_get_is_initialized(dev) > 0 ? LINK_UPDATE_MAX_RETRIES : 1;
+ 
+         for (i = 0; i < retries; i++) {
+-                _cleanup_free_ char *target = NULL;
+                 struct stat st1 = {}, st2 = {};
+ 
+                 r = stat(dirname, &st1);
diff --git a/debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch b/debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch
new file mode 100644
index 00000000..e3f1c642
--- /dev/null
+++ b/debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch
@@ -0,0 +1,47 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Sat, 25 Sep 2021 21:08:26 +0200
+Subject: Revert "udev: link_update() should fail if the entry in symlink dir
+ couldn't have been created"
+
+This reverts commit c07dc6cedc6e6fbc28a0da3e8c8b12900423b409.
+---
+ src/udev/udev-node.c | 21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
+index 2cc78c9..bde18f7 100644
+--- a/src/udev/udev-node.c
++++ b/src/udev/udev-node.c
+@@ -214,23 +214,20 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
+         if (!filename)
+                 return log_oom();
+ 
+-        if (!add) {
+-                if (unlink(filename) == 0)
+-                        (void) rmdir(dirname);
+-        } else
+-                for (;;) {
++        if (!add && unlink(filename) == 0)
++                (void) rmdir(dirname);
++
++        if (add)
++                do {
+                         _cleanup_close_ int fd = -1;
+ 
+                         r = mkdir_parents(filename, 0755);
+                         if (!IN_SET(r, 0, -ENOENT))
+-                                return r;
+-
+-                        fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
+-                        if (fd >= 0)
+                                 break;
+-                        if (errno != ENOENT)
+-                                return -errno;
+-                }
++                        fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
++                        if (fd < 0)
++                                r = -errno;
++                } while (r == -ENOENT);
+ 
+         /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink
+          * will be fixed in the second invocation. */
diff --git a/debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch b/debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch
new file mode 100644
index 00000000..36bdbb55
--- /dev/null
+++ b/debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch
@@ -0,0 +1,163 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Sat, 25 Sep 2021 21:08:36 +0200
+Subject: Revert "udev: make algorithm that selects highest priority devlink
+ less susceptible to race conditions"
+
+This reverts commit 30f6dce62cb3a738b20253f2192270607c31b55b.
+---
+ src/udev/udev-event.c |  7 -----
+ src/udev/udev-node.c  | 75 +++++++++++----------------------------------------
+ 2 files changed, 15 insertions(+), 67 deletions(-)
+
+diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
+index 5159d19..9cf5190 100644
+--- a/src/udev/udev-event.c
++++ b/src/udev/udev-event.c
+@@ -1041,13 +1041,6 @@ int udev_event_execute_rules(UdevEvent *event,
+         if (r < 0)
+                 return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+ 
+-        /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database,
+-         * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure
+-         * symlinks point to devices that claim them with the highest priority. */
+-        r = update_devnode(event);
+-        if (r < 0)
+-                return r;
+-
+         device_set_is_initialized(dev);
+ 
+         return 0;
+diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
+index bde18f7..9d4b7d9 100644
+--- a/src/udev/udev-node.c
++++ b/src/udev/udev-node.c
+@@ -20,15 +20,12 @@
+ #include "path-util.h"
+ #include "selinux-util.h"
+ #include "smack-util.h"
+-#include "stat-util.h"
+ #include "stdio-util.h"
+ #include "string-util.h"
+ #include "strxcpyx.h"
+ #include "udev-node.h"
+ #include "user-util.h"
+ 
+-#define LINK_UPDATE_MAX_RETRIES 128
+-
+ static int node_symlink(sd_device *dev, const char *node, const char *slink) {
+         _cleanup_free_ char *slink_dirname = NULL, *target = NULL;
+         const char *id_filename, *slink_tmp;
+@@ -102,9 +99,7 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) {
+         if (rename(slink_tmp, slink) < 0) {
+                 r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
+                 (void) unlink(slink_tmp);
+-        } else
+-                /* Tell caller that we replaced already existing symlink. */
+-                r = 1;
++        }
+ 
+         return r;
+ }
+@@ -197,7 +192,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
+         _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL;
+         char name_enc[PATH_MAX];
+         const char *id_filename;
+-        int i, r, retries;
++        int r;
+ 
+         assert(dev);
+         assert(slink);
+@@ -217,6 +212,14 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
+         if (!add && unlink(filename) == 0)
+                 (void) rmdir(dirname);
+ 
++        r = link_find_prioritized(dev, add, dirname, &target);
++        if (r < 0) {
++                log_device_debug(dev, "No reference left, removing '%s'", slink);
++                if (unlink(slink) == 0)
++                        (void) rmdir_parents(slink, "/");
++        } else
++                (void) node_symlink(dev, target, slink);
++
+         if (add)
+                 do {
+                         _cleanup_close_ int fd = -1;
+@@ -229,49 +232,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
+                                 r = -errno;
+                 } while (r == -ENOENT);
+ 
+-        /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink
+-         * will be fixed in the second invocation. */
+-        retries = sd_device_get_is_initialized(dev) > 0 ? LINK_UPDATE_MAX_RETRIES : 1;
+-
+-        for (i = 0; i < retries; i++) {
+-                struct stat st1 = {}, st2 = {};
+-
+-                r = stat(dirname, &st1);
+-                if (r < 0 && errno != ENOENT)
+-                        return -errno;
+-
+-                r = link_find_prioritized(dev, add, dirname, &target);
+-                if (r == -ENOENT) {
+-                        log_device_debug(dev, "No reference left, removing '%s'", slink);
+-                        if (unlink(slink) == 0)
+-                                (void) rmdir_parents(slink, "/");
+-
+-                        break;
+-                } else if (r < 0)
+-                        return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m");
+-
+-                r = node_symlink(dev, target, slink);
+-                if (r < 0) {
+-                        (void) unlink(filename);
+-                        break;
+-                } else if (r == 1)
+-                        /* We have replaced already existing symlink, possibly there is some other device trying
+-                         * to claim the same symlink. Let's do one more iteration to give us a chance to fix
+-                         * the error if other device actually claims the symlink with higher priority. */
+-                        continue;
+-
+-                /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */
+-                if ((st1.st_mode & S_IFMT) != 0) {
+-                        r = stat(dirname, &st2);
+-                        if (r < 0 && errno != ENOENT)
+-                                return -errno;
+-
+-                        if (stat_inode_unmodified(&st1, &st2))
+-                                break;
+-                }
+-        }
+-
+-        return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP;
++        return r;
+ }
+ 
+ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
+@@ -490,11 +451,8 @@ int udev_node_add(sd_device *dev, bool apply,
+         (void) node_symlink(dev, devnode, filename);
+ 
+         /* create/update symlinks, add symlinks to name index */
+-        FOREACH_DEVICE_DEVLINK(dev, devlink) {
+-                r = link_update(dev, devlink, true);
+-                if (r < 0)
+-                        log_device_info_errno(dev, r, "Failed to update device symlinks: %m");
+-        }
++        FOREACH_DEVICE_DEVLINK(dev, devlink)
++                (void) link_update(dev, devlink, true);
+ 
+         return 0;
+ }
+@@ -507,11 +465,8 @@ int udev_node_remove(sd_device *dev) {
+         assert(dev);
+ 
+         /* remove/update symlinks, remove symlinks from name index */
+-        FOREACH_DEVICE_DEVLINK(dev, devlink) {
+-                r = link_update(dev, devlink, false);
+-                if (r < 0)
+-                        log_device_info_errno(dev, r, "Failed to update device symlinks: %m");
+-        }
++        FOREACH_DEVICE_DEVLINK(dev, devlink)
++                (void) link_update(dev, devlink, false);
+ 
+         r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
+         if (r < 0)
diff --git a/debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch b/debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch
new file mode 100644
index 00000000..50ae9741
--- /dev/null
+++ b/debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch
@@ -0,0 +1,28 @@
+From: Luca Boccassi <luca.boccassi@microsoft.com>
+Date: Tue, 15 Dec 2020 18:26:34 +0000
+Subject: machine: adjust error message to use 'normalized' instead of ../
+
+(cherry picked from commit 724e689715c8d9f23d035ab20d8c87b6b6c06e33)
+---
+ src/machine/machine-dbus.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
+index 3c8f4fd..5ed892f 100644
+--- a/src/machine/machine-dbus.c
++++ b/src/machine/machine-dbus.c
+@@ -827,12 +827,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
+                 return r;
+ 
+         if (!path_is_absolute(src) || !path_is_normalized(src))
+-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
++                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
+ 
+         if (isempty(dest))
+                 dest = src;
+         else if (!path_is_absolute(dest) || !path_is_normalized(dest))
+-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
++                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
+ 
+         r = bus_verify_polkit_async(
+                         message,
diff --git a/debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch b/debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch
new file mode 100644
index 00000000..965e925e
--- /dev/null
+++ b/debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch
@@ -0,0 +1,499 @@
+From: Luca Boccassi <luca.boccassi@microsoft.com>
+Date: Thu, 13 Aug 2020 14:01:34 +0100
+Subject: machine/basic: factor out helper function to add airlocked mount to
+ namespace
+
+(cherry picked from commit 6af52c3a458691b016bedeba34c1e72294a67c81)
+---
+ src/machine/machine-dbus.c | 214 ++------------------------------------------
+ src/shared/mount-util.c    | 217 +++++++++++++++++++++++++++++++++++++++++++++
+ src/shared/mount-util.h    |   2 +
+ 3 files changed, 227 insertions(+), 206 deletions(-)
+
+diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
+index 1105008..3c8f4fd 100644
+--- a/src/machine/machine-dbus.c
++++ b/src/machine/machine-dbus.c
+@@ -810,17 +810,9 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
+ }
+ 
+ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+-        _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
+-        char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
+-        bool mount_slave_created = false, mount_slave_mounted = false,
+-                mount_tmp_created = false, mount_tmp_mounted = false,
+-                mount_outside_created = false, mount_outside_mounted = false;
+-        _cleanup_free_ char *chased_src = NULL;
+         int read_only, make_file_or_directory;
+-        const char *dest, *src;
++        const char *dest, *src, *propagate_directory;
+         Machine *m = userdata;
+-        struct stat st;
+-        pid_t child;
+         uid_t uid;
+         int r;
+ 
+@@ -862,205 +854,15 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
+         if (uid != 0)
+                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
+ 
+-        /* One day, when bind mounting /proc/self/fd/n works across
+-         * namespace boundaries we should rework this logic to make
+-         * use of it... */
+-
+-        p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/");
+-        if (laccess(p, F_OK) < 0)
+-                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points.");
+-
+-        r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, &chased_src, NULL);
++        propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name);
++        r = bind_mount_in_namespace(m->leader,
++                                    propagate_directory,
++                                    "/run/host/incoming/",
++                                    src, dest, read_only, make_file_or_directory);
+         if (r < 0)
+-                return sd_bus_error_set_errnof(error, r, "Failed to resolve source path: %m");
+-
+-        if (lstat(chased_src, &st) < 0)
+-                return sd_bus_error_set_errnof(error, errno, "Failed to stat() source path: %m");
+-        if (S_ISLNK(st.st_mode)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safe… */
+-                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Source directory can't be a symbolic link");
+-
+-        /* Our goal is to install a new bind mount into the container,
+-           possibly read-only. This is irritatingly complex
+-           unfortunately, currently.
+-
+-           First, we start by creating a private playground in /tmp,
+-           that we can mount MS_SLAVE. (Which is necessary, since
+-           MS_MOVE cannot be applied to mounts with MS_SHARED parent
+-           mounts.) */
+-
+-        if (!mkdtemp(mount_slave))
+-                return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave);
+-
+-        mount_slave_created = true;
+-
+-        r = mount_nofollow_verbose(LOG_DEBUG, mount_slave, mount_slave, NULL, MS_BIND, NULL);
+-        if (r < 0) {
+-                sd_bus_error_set_errnof(error, r, "Failed to make bind mount %s: %m", mount_slave);
+-                goto finish;
+-        }
+-
+-        mount_slave_mounted = true;
+-
+-        r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_slave, NULL, MS_SLAVE, NULL);
+-        if (r < 0) {
+-                sd_bus_error_set_errnof(error, r, "Failed to remount slave %s: %m", mount_slave);
+-                goto finish;
+-        }
+-
+-        /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
+-        mount_tmp = strjoina(mount_slave, "/mount");
+-        r = make_mount_point_inode_from_stat(&st, mount_tmp, 0700);
+-        if (r < 0) {
+-                sd_bus_error_set_errnof(error, r, "Failed to create temporary mount point %s: %m", mount_tmp);
+-                goto finish;
+-        }
+-
+-        mount_tmp_created = true;
+-
+-        r = mount_nofollow_verbose(LOG_DEBUG, chased_src, mount_tmp, NULL, MS_BIND, NULL);
+-        if (r < 0) {
+-                sd_bus_error_set_errnof(error, r, "Failed to mount %s: %m", chased_src);
+-                goto finish;
+-        }
+-
+-        mount_tmp_mounted = true;
+-
+-        /* Third, we remount the new bind mount read-only if requested. */
+-        if (read_only) {
+-                r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
+-                if (r < 0) {
+-                        sd_bus_error_set_errnof(error, r, "Failed to remount read-only %s: %m", mount_tmp);
+-                        goto finish;
+-                }
+-        }
+-
+-        /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only
+-         * right-away. */
+-
+-        mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX");
+-        if (S_ISDIR(st.st_mode))
+-                r = mkdtemp(mount_outside) ? 0 : -errno;
+-        else {
+-                r = mkostemp_safe(mount_outside);
+-                safe_close(r);
+-        }
+-        if (r < 0) {
+-                sd_bus_error_set_errnof(error, r, "Cannot create propagation file or directory %s: %m", mount_outside);
+-                goto finish;
+-        }
+-
+-        mount_outside_created = true;
+-
+-        r = mount_nofollow_verbose(LOG_DEBUG, mount_tmp, mount_outside, NULL, MS_MOVE, NULL);
+-        if (r < 0) {
+-                sd_bus_error_set_errnof(error, r, "Failed to move %s to %s: %m", mount_tmp, mount_outside);
+-                goto finish;
+-        }
+-
+-        mount_outside_mounted = true;
+-        mount_tmp_mounted = false;
+-
+-        if (S_ISDIR(st.st_mode))
+-                (void) rmdir(mount_tmp);
+-        else
+-                (void) unlink(mount_tmp);
+-        mount_tmp_created = false;
+-
+-        (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW);
+-        mount_slave_mounted = false;
+-
+-        (void) rmdir(mount_slave);
+-        mount_slave_created = false;
+-
+-        if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) {
+-                r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
+-                goto finish;
+-        }
+-
+-        r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child);
+-        if (r < 0) {
+-                sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+-                goto finish;
+-        }
+-        if (r == 0) {
+-                const char *mount_inside, *q;
+-                int mntfd;
+-
+-                errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+-
+-                q = procfs_file_alloca(m->leader, "ns/mnt");
+-                mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+-                if (mntfd < 0) {
+-                        r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
+-                        goto child_fail;
+-                }
+-
+-                if (setns(mntfd, CLONE_NEWNS) < 0) {
+-                        r = log_error_errno(errno, "Failed to join namespace of leader: %m");
+-                        goto child_fail;
+-                }
+-
+-                if (make_file_or_directory) {
+-                        (void) mkdir_parents(dest, 0755);
+-                        (void) make_mount_point_inode_from_stat(&st, dest, 0700);
+-                }
+-
+-                mount_inside = strjoina("/run/host/incoming/", basename(mount_outside));
+-                r = mount_nofollow_verbose(LOG_ERR, mount_inside, dest, NULL, MS_MOVE, NULL);
+-                if (r < 0)
+-                        goto child_fail;
+-
+-                _exit(EXIT_SUCCESS);
+-
+-        child_fail:
+-                (void) write(errno_pipe_fd[1], &r, sizeof(r));
+-                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+-
+-                _exit(EXIT_FAILURE);
+-        }
+-
+-        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
++                return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in machine's namespace: %m", src, dest);
+ 
+-        r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0);
+-        if (r < 0) {
+-                r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+-                goto finish;
+-        }
+-        if (r != EXIT_SUCCESS) {
+-                if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
+-                        r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m");
+-                else
+-                        r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child failed.");
+-                goto finish;
+-        }
+-
+-        r = sd_bus_reply_method_return(message, NULL);
+-
+-finish:
+-        if (mount_outside_mounted)
+-                (void) umount_verbose(LOG_DEBUG, mount_outside, UMOUNT_NOFOLLOW);
+-        if (mount_outside_created) {
+-                if (S_ISDIR(st.st_mode))
+-                        (void) rmdir(mount_outside);
+-                else
+-                        (void) unlink(mount_outside);
+-        }
+-
+-        if (mount_tmp_mounted)
+-                (void) umount_verbose(LOG_DEBUG, mount_tmp, UMOUNT_NOFOLLOW);
+-        if (mount_tmp_created) {
+-                if (S_ISDIR(st.st_mode))
+-                        (void) rmdir(mount_tmp);
+-                else
+-                        (void) unlink(mount_tmp);
+-        }
+-
+-        if (mount_slave_mounted)
+-                (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW);
+-        if (mount_slave_created)
+-                (void) rmdir(mount_slave);
+-
+-        return r;
++        return sd_bus_reply_method_return(message, NULL);
+ }
+ 
+ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c
+index b19b384..4cfbb55 100644
+--- a/src/shared/mount-util.c
++++ b/src/shared/mount-util.c
+@@ -14,15 +14,18 @@
+ #include "fs-util.h"
+ #include "hashmap.h"
+ #include "libmount-util.h"
++#include "mkdir.h"
+ #include "mount-util.h"
+ #include "mountpoint-util.h"
+ #include "parse-util.h"
+ #include "path-util.h"
++#include "process-util.h"
+ #include "set.h"
+ #include "stat-util.h"
+ #include "stdio-util.h"
+ #include "string-util.h"
+ #include "strv.h"
++#include "tmpfile-util.h"
+ 
+ int mount_fd(const char *source,
+              int target_fd,
+@@ -742,3 +745,217 @@ int mount_option_mangle(
+ 
+         return 0;
+ }
++
++int bind_mount_in_namespace(
++                pid_t target,
++                const char *propagate_path,
++                const char *incoming_path,
++                const char *src,
++                const char *dest,
++                bool read_only,
++                bool make_file_or_directory) {
++
++        _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
++        char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
++        bool mount_slave_created = false, mount_slave_mounted = false,
++                mount_tmp_created = false, mount_tmp_mounted = false,
++                mount_outside_created = false, mount_outside_mounted = false;
++        _cleanup_free_ char *chased_src = NULL;
++        struct stat st;
++        pid_t child;
++        int r;
++
++        assert(target > 0);
++        assert(propagate_path);
++        assert(incoming_path);
++        assert(src);
++        assert(dest);
++
++        /* One day, when bind mounting /proc/self/fd/n works across
++         * namespace boundaries we should rework this logic to make
++         * use of it... */
++
++        p = strjoina(propagate_path, "/");
++        r = laccess(p, F_OK);
++        if (r < 0)
++                return log_debug_errno(r == -ENOENT ? SYNTHETIC_ERRNO(EOPNOTSUPP) : r, "Target does not allow propagation of mount points");
++
++        r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, &chased_src, NULL);
++        if (r < 0)
++                return log_debug_errno(r, "Failed to resolve source path of %s: %m", src);
++
++        if (lstat(chased_src, &st) < 0)
++                return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", chased_src);
++        if (S_ISLNK(st.st_mode)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safe… */
++                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Source directory %s can't be a symbolic link", chased_src);
++
++        /* Our goal is to install a new bind mount into the container,
++           possibly read-only. This is irritatingly complex
++           unfortunately, currently.
++
++           First, we start by creating a private playground in /tmp,
++           that we can mount MS_SLAVE. (Which is necessary, since
++           MS_MOVE cannot be applied to mounts with MS_SHARED parent
++           mounts.) */
++
++        if (!mkdtemp(mount_slave))
++                return log_debug_errno(errno, "Failed to create playground %s: %m", mount_slave);
++
++        mount_slave_created = true;
++
++        r = mount_nofollow_verbose(LOG_DEBUG, mount_slave, mount_slave, NULL, MS_BIND, NULL);
++        if (r < 0)
++                goto finish;
++
++        mount_slave_mounted = true;
++
++        r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_slave, NULL, MS_SLAVE, NULL);
++        if (r < 0)
++                goto finish;
++
++        /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
++        mount_tmp = strjoina(mount_slave, "/mount");
++        r = make_mount_point_inode_from_stat(&st, mount_tmp, 0700);
++        if (r < 0) {
++                log_debug_errno(r, "Failed to create temporary mount point %s: %m", mount_tmp);
++                goto finish;
++        }
++
++        mount_tmp_created = true;
++
++        r = mount_nofollow_verbose(LOG_DEBUG, chased_src, mount_tmp, NULL, MS_BIND, NULL);
++        if (r < 0)
++                goto finish;
++
++        mount_tmp_mounted = true;
++
++        /* Third, we remount the new bind mount read-only if requested. */
++        if (read_only) {
++                r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
++                if (r < 0)
++                        goto finish;
++        }
++
++        /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only
++         * right-away. */
++
++        mount_outside = strjoina(propagate_path, "/XXXXXX");
++        if (S_ISDIR(st.st_mode))
++                r = mkdtemp(mount_outside) ? 0 : -errno;
++        else {
++                r = mkostemp_safe(mount_outside);
++                safe_close(r);
++        }
++        if (r < 0) {
++                log_debug_errno(r, "Cannot create propagation file or directory %s: %m", mount_outside);
++                goto finish;
++        }
++
++        mount_outside_created = true;
++
++        r = mount_nofollow_verbose(LOG_DEBUG, mount_tmp, mount_outside, NULL, MS_MOVE, NULL);
++        if (r < 0)
++                goto finish;
++
++        mount_outside_mounted = true;
++        mount_tmp_mounted = false;
++
++        if (S_ISDIR(st.st_mode))
++                (void) rmdir(mount_tmp);
++        else
++                (void) unlink(mount_tmp);
++        mount_tmp_created = false;
++
++        (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW);
++        mount_slave_mounted = false;
++
++        (void) rmdir(mount_slave);
++        mount_slave_created = false;
++
++        if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) {
++                log_debug_errno(errno, "Failed to create pipe: %m");
++                goto finish;
++        }
++
++        r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child);
++        if (r < 0)
++                goto finish;
++        if (r == 0) {
++                const char *mount_inside, *q;
++                int mntfd;
++
++                errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
++
++                q = procfs_file_alloca(target, "ns/mnt");
++                mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
++                if (mntfd < 0) {
++                        r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
++                        goto child_fail;
++                }
++
++                if (setns(mntfd, CLONE_NEWNS) < 0) {
++                        r = log_error_errno(errno, "Failed to join namespace of leader: %m");
++                        goto child_fail;
++                }
++
++                if (make_file_or_directory) {
++                        (void) mkdir_parents(dest, 0755);
++                        (void) make_mount_point_inode_from_stat(&st, dest, 0700);
++                }
++
++                /* Fifth, move the mount to the right place inside */
++                mount_inside = strjoina(incoming_path, basename(mount_outside));
++                r = mount_nofollow_verbose(LOG_ERR, mount_inside, dest, NULL, MS_MOVE, NULL);
++                if (r < 0)
++                        goto child_fail;
++
++                _exit(EXIT_SUCCESS);
++
++        child_fail:
++                (void) write(errno_pipe_fd[1], &r, sizeof(r));
++                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
++
++                _exit(EXIT_FAILURE);
++        }
++
++        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
++
++        r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0);
++        if (r < 0) {
++                log_debug_errno(r, "Failed to wait for child: %m");
++                goto finish;
++        }
++        if (r != EXIT_SUCCESS) {
++                if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
++                        log_debug_errno(r, "Failed to mount: %m");
++                else
++                        log_debug("Child failed.");
++                goto finish;
++        }
++
++finish:
++        if (mount_outside_mounted)
++                (void) umount_verbose(LOG_DEBUG, mount_outside, UMOUNT_NOFOLLOW);
++        if (mount_outside_created) {
++                if (S_ISDIR(st.st_mode))
++                        (void) rmdir(mount_outside);
++                else
++                        (void) unlink(mount_outside);
++        }
++
++        if (mount_tmp_mounted)
++                (void) umount_verbose(LOG_DEBUG, mount_tmp, UMOUNT_NOFOLLOW);
++        if (mount_tmp_created) {
++                if (S_ISDIR(st.st_mode))
++                        (void) rmdir(mount_tmp);
++                else
++                        (void) unlink(mount_tmp);
++        }
++
++        if (mount_slave_mounted)
++                (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW);
++        if (mount_slave_created)
++                (void) rmdir(mount_slave);
++
++        return r;
++}
+diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h
+index 6202008..c3500a0 100644
+--- a/src/shared/mount-util.h
++++ b/src/shared/mount-util.h
+@@ -97,3 +97,5 @@ static inline char* umount_and_rmdir_and_free(char *p) {
+         return NULL;
+ }
+ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free);
++
++int bind_mount_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory);
diff --git a/debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch b/debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch
new file mode 100644
index 00000000..e9cd9dde
--- /dev/null
+++ b/debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch
@@ -0,0 +1,105 @@
+From: Luca Boccassi <bluca@debian.org>
+Date: Wed, 13 Jan 2021 23:52:00 +0000
+Subject: machine: enter target PID namespace when adding a live mount
+
+machinectl fails since 21935150a0c42b91a322105f6a9129116bfc8e2e as it's now
+mounting onto a file descriptor in a target namespace, without joining the
+target's PID namespace.
+Note that it's not enough to setns CLONE_NEWPID, but a double-fork is required
+as well, as implemented by namespace_fork().
+
+Add a test case to TEST-13-NSPAWN to cover this use case.
+
+(cherry picked from commit 98f654fdeab1e1b6df2be76e29e4ccbb6624898d)
+---
+ src/shared/mount-util.c       |  6 +++---
+ test/create-busybox-container |  3 +++
+ test/units/testsuite-13.sh    | 25 +++++++++++++++++++++++++
+ 3 files changed, 31 insertions(+), 3 deletions(-)
+
+diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c
+index 368c5f0..2e374cc 100644
+--- a/src/shared/mount-util.c
++++ b/src/shared/mount-util.c
+@@ -757,7 +757,7 @@ int bind_mount_in_namespace(
+                 bool make_file_or_directory) {
+ 
+         _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
+-        _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1;
++        _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1, pidns_fd = -1;
+         char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
+         bool mount_slave_created = false, mount_slave_mounted = false,
+                 mount_tmp_created = false, mount_tmp_mounted = false,
+@@ -773,7 +773,7 @@ int bind_mount_in_namespace(
+         assert(src);
+         assert(dest);
+ 
+-        r = namespace_open(target, NULL, &mntns_fd, NULL, NULL, &root_fd);
++        r = namespace_open(target, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
+         if (r < 0)
+                 return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m");
+ 
+@@ -898,7 +898,7 @@ int bind_mount_in_namespace(
+         }
+ 
+         r = namespace_fork("(sd-bindmnt)", "(sd-bindmnt-inner)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+-                           -1, mntns_fd, -1, -1, root_fd, &child);
++                           pidns_fd, mntns_fd, -1, -1, root_fd, &child);
+         if (r < 0)
+                 goto finish;
+         if (r == 0) {
+diff --git a/test/create-busybox-container b/test/create-busybox-container
+index 5ded429..b2b7b26 100755
+--- a/test/create-busybox-container
++++ b/test/create-busybox-container
+@@ -28,6 +28,9 @@ ln -s busybox "$root/bin/cat"
+ ln -s busybox "$root/bin/tr"
+ ln -s busybox "$root/bin/ps"
+ ln -s busybox "$root/bin/ip"
++ln -s busybox "$root/bin/seq"
++ln -s busybox "$root/bin/sleep"
++ln -s busybox "$root/bin/test"
+ 
+ mkdir -p "$root/sbin"
+ cat <<'EOF' >"$root/sbin/init"
+diff --git a/test/units/testsuite-13.sh b/test/units/testsuite-13.sh
+index 969ca4a..1844323 100755
+--- a/test/units/testsuite-13.sh
++++ b/test/units/testsuite-13.sh
+@@ -93,6 +93,29 @@ if echo test >> /run/host/os-release; then exit 1; fi
+     fi
+ }
+ 
++function check_machinectl_bind {
++    local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep 0.5; done; exit 1;'
++
++    cat <<EOF > /run/systemd/system/nspawn_machinectl_bind.service
++[Service]
++Type=notify
++ExecStart=systemd-nspawn $SUSE_OPTS -D /testsuite-13.nc-container --notify-ready=no /bin/sh -x -e -c "$_cmd"
++EOF
++
++    systemctl start nspawn_machinectl_bind.service
++
++    touch /tmp/marker
++
++    machinectl bind --mkdir testsuite-13.nc-container /tmp/marker
++
++    while systemctl show -P SubState nspawn_machinectl_bind.service | grep -q running
++    do
++        sleep 0.1
++    done
++
++    return $(systemctl show -P ExecMainStatus nspawn_machinectl_bind.service)
++}
++
+ function run {
+     if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then
+         printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2
+@@ -186,4 +209,6 @@ for api_vfs_writable in yes no network; do
+     run yes yes $api_vfs_writable
+ done
+ 
++check_machinectl_bind
++
+ touch /testok
diff --git a/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch b/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch
new file mode 100644
index 00000000..67d959c7
--- /dev/null
+++ b/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch
@@ -0,0 +1,128 @@
+From: Lennart Poettering <lennart@poettering.net>
+Date: Tue, 26 Jan 2021 16:47:07 +0100
+Subject: rm-rf: fstatat() might fail if containing dir has limited access
+ mode, patch that too
+
+(cherry picked from commit 1b55621dabf741dd963f59ac706ea62cd6e3e95c)
+(cherry picked from commit ce53b81a600e2162ee86e2f4d202e7f28eceb2c6)
+---
+ src/basic/rm-rf.c | 82 ++++++++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 66 insertions(+), 16 deletions(-)
+
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index 4c39ce8..2f2ebc3 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -23,13 +23,38 @@ static bool is_physical_fs(const struct statfs *sfs) {
+         return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
+ }
+ 
++static int patch_dirfd_mode(
++                int dfd,
++                mode_t *ret_old_mode) {
++
++        struct stat st;
++
++        assert(dfd >= 0);
++        assert(ret_old_mode);
++
++        if (fstat(dfd, &st) < 0)
++                return -errno;
++        if (!S_ISDIR(st.st_mode))
++                return -ENOTDIR;
++        if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */
++                return -EACCES; /* original error */
++        if (st.st_uid != geteuid())  /* this only works if the UID matches ours */
++                return -EACCES;
++
++        if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0)
++                return -errno;
++
++        *ret_old_mode = st.st_mode;
++        return 0;
++}
++
+ static int unlinkat_harder(
+                 int dfd,
+                 const char *filename,
+                 int unlink_flags,
+                 RemoveFlags remove_flags) {
+ 
+-        struct stat st;
++        mode_t old_mode;
+         int r;
+ 
+         /* Like unlinkat(), but tries harder: if we get EACCESS we'll try to set the r/w/x bits on the
+@@ -41,22 +66,46 @@ static int unlinkat_harder(
+         if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD))
+                 return -errno;
+ 
+-        if (fstat(dfd, &st) < 0)
+-                return -errno;
+-        if (!S_ISDIR(st.st_mode))
+-                return -ENOTDIR;
+-        if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */
+-                return -EACCES; /* original error */
+-        if (st.st_uid != geteuid())  /* this only works if the UID matches ours */
+-                return -EACCES;
+-
+-        if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0)
+-                return -errno;
++        r = patch_dirfd_mode(dfd, &old_mode);
++        if (r < 0)
++                return r;
+ 
+         if (unlinkat(dfd, filename, unlink_flags) < 0) {
+                 r = -errno;
+                 /* Try to restore the original access mode if this didn't work */
+-                (void) fchmod(dfd, st.st_mode & 07777);
++                (void) fchmod(dfd, old_mode);
++                return r;
++        }
++
++        /* If this worked, we won't reset the old mode, since we'll need it for other entries too, and we
++         * should destroy the whole thing */
++        return 0;
++}
++
++static int fstatat_harder(
++                int dfd,
++                const char *filename,
++                struct stat *ret,
++                int fstatat_flags,
++                RemoveFlags remove_flags) {
++
++        mode_t old_mode;
++        int r;
++
++        /* Like unlink_harder() but does the same for fstatat() */
++
++        if (fstatat(dfd, filename, ret, fstatat_flags) >= 0)
++                return 0;
++        if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD))
++                return -errno;
++
++        r = patch_dirfd_mode(dfd, &old_mode);
++        if (r < 0)
++                return r;
++
++        if (fstatat(dfd, filename, ret, fstatat_flags) < 0) {
++                r = -errno;
++                (void) fchmod(dfd, old_mode);
+                 return r;
+         }
+ 
+@@ -112,9 +161,10 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
+ 
+                 if (de->d_type == DT_UNKNOWN ||
+                     (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
+-                        if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+-                                if (ret == 0 && errno != ENOENT)
+-                                        ret = -errno;
++                        r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags);
++                        if (r < 0) {
++                                if (ret == 0 && r != -ENOENT)
++                                        ret = r;
+                                 continue;
+                         }
+ 
diff --git a/debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch b/debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch
new file mode 100644
index 00000000..66a1ef58
--- /dev/null
+++ b/debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch
@@ -0,0 +1,39 @@
+From: Lennart Poettering <lennart@poettering.net>
+Date: Tue, 5 Oct 2021 10:32:56 +0200
+Subject: rm-rf: optionally fsync() after removing directory tree
+
+(cherry picked from commit bdfe7ada0d4d66e6d6e65f2822acbb1ec230f9c2)
+(cherry picked from commit 2426beacca09d84091759be45b25c88116302184)
+(cherry picked from commit 0e180f8e9c25c707b0465ad1b9447a4360f785f1)
+(cherry picked from commit 9a9c2220cd3cb61c2de9c482f8ed7fa60807b14a)
+---
+ src/basic/rm-rf.c | 3 +++
+ src/basic/rm-rf.h | 1 +
+ 2 files changed, 4 insertions(+)
+
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index f1b8445..cf671c2 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -249,6 +249,9 @@ int rm_rf_children(
+                         ret = r;
+         }
+ 
++        if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0)
++                ret = -errno;
++
+         return ret;
+ }
+ 
+diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
+index b0d5b63..2619fc5 100644
+--- a/src/basic/rm-rf.h
++++ b/src/basic/rm-rf.h
+@@ -12,6 +12,7 @@ typedef enum RemoveFlags {
+         REMOVE_SUBVOLUME        = 1 << 3, /* Drop btrfs subvolumes in the tree too */
+         REMOVE_MISSING_OK       = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
+         REMOVE_CHMOD            = 1 << 5, /* chmod() for write access if we cannot delete something */
++        REMOVE_SYNCFS           = 1 << 6, /* syncfs() the root of the specified directory after removing everything in it */
+ } RemoveFlags;
+ 
+ int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev);
diff --git a/debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch b/debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch
new file mode 100644
index 00000000..8692c2f5
--- /dev/null
+++ b/debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch
@@ -0,0 +1,320 @@
+From: Lennart Poettering <lennart@poettering.net>
+Date: Tue, 26 Jan 2021 16:30:06 +0100
+Subject: rm-rf: refactor rm_rf_children(),
+ split out body of directory iteration loop
+
+This splits out rm_rf_children_inner() as body of the loop. We can use
+that to implement rm_rf_child() for deleting one specific entry in a
+directory.
+
+(cherry picked from commit 1f0fb7d544711248cba34615e43c5a76bc902d74)
+(cherry picked from commit ca4a0e7d41f0b2a1fe2f99dbc3763187c16cf7ab)
+(cherry picked from commit 85ccac3393e78d4bf2776ffb8c3a1d8a2a909a2a)
+(cherry picked from commit a87d7ff1a60fe359978e12eb34224255a8f33e27)
+---
+ src/basic/rm-rf.c | 223 +++++++++++++++++++++++++++++++-----------------------
+ src/basic/rm-rf.h |   3 +-
+ 2 files changed, 131 insertions(+), 95 deletions(-)
+
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index 2f2ebc3..f1b8445 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -19,6 +19,9 @@
+ #include "stat-util.h"
+ #include "string-util.h"
+ 
++/* We treat tmpfs/ramfs + cgroupfs as non-physical file sytems. cgroupfs is similar to tmpfs in a way after
++ * all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it to remove
++ * those again. */
+ static bool is_physical_fs(const struct statfs *sfs) {
+         return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
+ }
+@@ -112,133 +115,145 @@ static int fstatat_harder(
+         return 0;
+ }
+ 
+-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
+-        _cleanup_closedir_ DIR *d = NULL;
+-        struct dirent *de;
+-        int ret = 0, r;
+-        struct statfs sfs;
++static int rm_rf_children_inner(
++                int fd,
++                const char *fname,
++                int is_dir,
++                RemoveFlags flags,
++                const struct stat *root_dev) {
+ 
+-        assert(fd >= 0);
++        struct stat st;
++        int r;
+ 
+-        /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
+-         * fd, in all cases, including on failure.. */
++        assert(fd >= 0);
++        assert(fname);
+ 
+-        if (!(flags & REMOVE_PHYSICAL)) {
++        if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
+ 
+-                r = fstatfs(fd, &sfs);
+-                if (r < 0) {
+-                        safe_close(fd);
+-                        return -errno;
+-                }
++                r = fstatat_harder(fd, fname, &st, AT_SYMLINK_NOFOLLOW, flags);
++                if (r < 0)
++                        return r;
+ 
+-                if (is_physical_fs(&sfs)) {
+-                        /* We refuse to clean physical file systems with this call,
+-                         * unless explicitly requested. This is extra paranoia just
+-                         * to be sure we never ever remove non-state data. */
+-                        _cleanup_free_ char *path = NULL;
++                is_dir = S_ISDIR(st.st_mode);
++        }
+ 
+-                        (void) fd_get_path(fd, &path);
+-                        log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.",
+-                                  strna(path));
++        if (is_dir) {
++                _cleanup_close_ int subdir_fd = -1;
++                int q;
+ 
+-                        safe_close(fd);
+-                        return -EPERM;
+-                }
+-        }
++                /* if root_dev is set, remove subdirectories only if device is same */
++                if (root_dev && st.st_dev != root_dev->st_dev)
++                        return 0;
+ 
+-        d = fdopendir(fd);
+-        if (!d) {
+-                safe_close(fd);
+-                return errno == ENOENT ? 0 : -errno;
+-        }
++                /* Stop at mount points */
++                r = fd_is_mount_point(fd, fname, 0);
++                if (r < 0)
++                        return r;
++                if (r > 0)
++                        return 0;
+ 
+-        FOREACH_DIRENT_ALL(de, d, return -errno) {
+-                bool is_dir;
+-                struct stat st;
++                if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
+ 
+-                if (dot_or_dot_dot(de->d_name))
+-                        continue;
++                        /* This could be a subvolume, try to remove it */
+ 
+-                if (de->d_type == DT_UNKNOWN ||
+-                    (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
+-                        r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags);
++                        r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
+                         if (r < 0) {
+-                                if (ret == 0 && r != -ENOENT)
+-                                        ret = r;
+-                                continue;
+-                        }
++                                if (!IN_SET(r, -ENOTTY, -EINVAL))
++                                        return r;
+ 
+-                        is_dir = S_ISDIR(st.st_mode);
+-                } else
+-                        is_dir = de->d_type == DT_DIR;
++                                /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
++                        } else
++                                /* It was a subvolume, done. */
++                                return 1;
++                }
+ 
+-                if (is_dir) {
+-                        _cleanup_close_ int subdir_fd = -1;
++                subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
++                if (subdir_fd < 0)
++                        return -errno;
+ 
+-                        /* if root_dev is set, remove subdirectories only if device is same */
+-                        if (root_dev && st.st_dev != root_dev->st_dev)
+-                                continue;
++                /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type
++                 * again for each directory */
++                q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
+ 
+-                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+-                        if (subdir_fd < 0) {
+-                                if (ret == 0 && errno != ENOENT)
+-                                        ret = -errno;
+-                                continue;
+-                        }
++                r = unlinkat_harder(fd, fname, AT_REMOVEDIR, flags);
++                if (r < 0)
++                        return r;
++                if (q < 0)
++                        return q;
+ 
+-                        /* Stop at mount points */
+-                        r = fd_is_mount_point(fd, de->d_name, 0);
+-                        if (r < 0) {
+-                                if (ret == 0 && r != -ENOENT)
+-                                        ret = r;
++                return 1;
+ 
+-                                continue;
+-                        }
+-                        if (r > 0)
+-                                continue;
++        } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
++                r = unlinkat_harder(fd, fname, 0, flags);
++                if (r < 0)
++                        return r;
+ 
+-                        if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
++                return 1;
++        }
+ 
+-                                /* This could be a subvolume, try to remove it */
++        return 0;
++}
+ 
+-                                r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
+-                                if (r < 0) {
+-                                        if (!IN_SET(r, -ENOTTY, -EINVAL)) {
+-                                                if (ret == 0)
+-                                                        ret = r;
++int rm_rf_children(
++                int fd,
++                RemoveFlags flags,
++                const struct stat *root_dev) {
+ 
+-                                                continue;
+-                                        }
++        _cleanup_closedir_ DIR *d = NULL;
++        struct dirent *de;
++        int ret = 0, r;
+ 
+-                                        /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
+-                                } else
+-                                        /* It was a subvolume, continue. */
+-                                        continue;
+-                        }
++        assert(fd >= 0);
++
++        /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
++         * fd, in all cases, including on failure. */
++
++        d = fdopendir(fd);
++        if (!d) {
++                safe_close(fd);
++                return -errno;
++        }
+ 
+-                        /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file
+-                         * system type again for each directory */
+-                        r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
+-                        if (r < 0 && ret == 0)
+-                                ret = r;
++        if (!(flags & REMOVE_PHYSICAL)) {
++                struct statfs sfs;
+ 
+-                        r = unlinkat_harder(fd, de->d_name, AT_REMOVEDIR, flags);
+-                        if (r < 0 && r != -ENOENT && ret == 0)
+-                                ret = r;
++                if (fstatfs(dirfd(d), &sfs) < 0)
++                        return -errno;
++
++                if (is_physical_fs(&sfs)) {
++                        /* We refuse to clean physical file systems with this call, unless explicitly
++                         * requested. This is extra paranoia just to be sure we never ever remove non-state
++                         * data. */
+ 
+-                } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
++                        _cleanup_free_ char *path = NULL;
+ 
+-                        r = unlinkat_harder(fd, de->d_name, 0, flags);
+-                        if (r < 0 && r != -ENOENT && ret == 0)
+-                                ret = r;
++                        (void) fd_get_path(fd, &path);
++                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
++                                               "Attempted to remove disk file system under \"%s\", and we can't allow that.",
++                                               strna(path));
+                 }
+         }
++
++        FOREACH_DIRENT_ALL(de, d, return -errno) {
++                int is_dir;
++
++                if (dot_or_dot_dot(de->d_name))
++                        continue;
++
++                is_dir =
++                        de->d_type == DT_UNKNOWN ? -1 :
++                        de->d_type == DT_DIR;
++
++                r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev);
++                if (r < 0 && r != -ENOENT && ret == 0)
++                        ret = r;
++        }
++
+         return ret;
+ }
+ 
+ int rm_rf(const char *path, RemoveFlags flags) {
+         int fd, r;
+-        struct statfs s;
+ 
+         assert(path);
+ 
+@@ -283,9 +298,10 @@ int rm_rf(const char *path, RemoveFlags flags) {
+                 if (FLAGS_SET(flags, REMOVE_ROOT)) {
+ 
+                         if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
++                                struct statfs s;
++
+                                 if (statfs(path, &s) < 0)
+                                         return -errno;
+-
+                                 if (is_physical_fs(&s))
+                                         return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+                                                                "Attempted to remove files from a disk file system under \"%s\", refusing.",
+@@ -313,3 +329,22 @@ int rm_rf(const char *path, RemoveFlags flags) {
+ 
+         return r;
+ }
++
++int rm_rf_child(int fd, const char *name, RemoveFlags flags) {
++
++        /* Removes one specific child of the specified directory */
++
++        if (fd < 0)
++                return -EBADF;
++
++        if (!filename_is_valid(name))
++                return -EINVAL;
++
++        if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */
++                return -EINVAL;
++
++        if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
++                return -EINVAL;
++
++        return rm_rf_children_inner(fd, name, -1, flags, NULL);
++}
+diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
+index ec56232..b0d5b63 100644
+--- a/src/basic/rm-rf.h
++++ b/src/basic/rm-rf.h
+@@ -14,7 +14,8 @@ typedef enum RemoveFlags {
+         REMOVE_CHMOD            = 1 << 5, /* chmod() for write access if we cannot delete something */
+ } RemoveFlags;
+ 
+-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
++int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev);
++int rm_rf_child(int fd, const char *name, RemoveFlags flags);
+ int rm_rf(const char *path, RemoveFlags flags);
+ 
+ /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
diff --git a/debian/patches/series b/debian/patches/series
index 07fb32da..e41de0e7 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -10,6 +10,20 @@ network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch
 unit-name-generate-a-clear-error-code-when-convertin.patch
 basic-unit-name-do-not-use-strdupa-on-a-path.patch
 basic-unit-name-adjust-comments.patch
+udevadm-trigger-do-not-return-immediately-on-EACCES.patch
+btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch
+rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch
+rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch
+rm-rf-optionally-fsync-after-removing-directory-tree.patch
+tmpfiles-st-may-have-been-used-uninitialized.patch
+shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch
+shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch
+shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch
+basic-add-make_mount_point_inode-helper.patch
+machine-basic-factor-out-helper-function-to-add-airlocked.patch
+machine-adjust-error-message-to-use-normalized-instead-of.patch
+shared-mount-util-use-namespace_fork-utils.patch
+machine-enter-target-PID-namespace-when-adding-a-live-mou.patch
 debian/Use-Debian-specific-config-files.patch
 debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch
 debian/Make-run-lock-tmpfs-an-API-fs.patch
@@ -32,3 +46,6 @@ debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch
 debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch
 debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch
 debian/Downgrade-a-couple-of-warnings-to-debug.patch
+debian/Revert-udev-fix-memleak.patch
+debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch
+debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch
diff --git a/debian/patches/shared-mount-util-use-namespace_fork-utils.patch b/debian/patches/shared-mount-util-use-namespace_fork-utils.patch
new file mode 100644
index 00000000..f870a3e3
--- /dev/null
+++ b/debian/patches/shared-mount-util-use-namespace_fork-utils.patch
@@ -0,0 +1,92 @@
+From: Luca Boccassi <luca.boccassi@microsoft.com>
+Date: Thu, 13 Aug 2020 14:47:01 +0100
+Subject: shared/mount-util: use namespace_fork utils
+
+(cherry picked from commit 2338a175fdec3859eab03115ca82a0d58453f5d7)
+---
+ src/shared/mount-util.c | 40 ++++++++++++++++++++++++----------------
+ 1 file changed, 24 insertions(+), 16 deletions(-)
+
+diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c
+index 4cfbb55..368c5f0 100644
+--- a/src/shared/mount-util.c
++++ b/src/shared/mount-util.c
+@@ -17,6 +17,7 @@
+ #include "mkdir.h"
+ #include "mount-util.h"
+ #include "mountpoint-util.h"
++#include "namespace-util.h"
+ #include "parse-util.h"
+ #include "path-util.h"
+ #include "process-util.h"
+@@ -756,12 +757,13 @@ int bind_mount_in_namespace(
+                 bool make_file_or_directory) {
+ 
+         _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
++        _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1;
+         char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
+         bool mount_slave_created = false, mount_slave_mounted = false,
+                 mount_tmp_created = false, mount_tmp_mounted = false,
+                 mount_outside_created = false, mount_outside_mounted = false;
+         _cleanup_free_ char *chased_src = NULL;
+-        struct stat st;
++        struct stat st, self_mntns_st;
+         pid_t child;
+         int r;
+ 
+@@ -771,6 +773,24 @@ int bind_mount_in_namespace(
+         assert(src);
+         assert(dest);
+ 
++        r = namespace_open(target, NULL, &mntns_fd, NULL, NULL, &root_fd);
++        if (r < 0)
++                return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m");
++
++        if (fstat(mntns_fd, &st) < 0)
++                return log_debug_errno(errno, "Failed to fstat mount namespace FD of target process: %m");
++
++        r = namespace_open(0, NULL, &self_mntns_fd, NULL, NULL, NULL);
++        if (r < 0)
++                return log_debug_errno(r, "Failed to retrieve FDs of systemd's namespace: %m");
++
++        if (fstat(self_mntns_fd, &self_mntns_st) < 0)
++                return log_debug_errno(errno, "Failed to fstat mount namespace FD of systemd: %m");
++
++        /* We can't add new mounts at runtime if the process wasn't started in a namespace */
++        if (st.st_ino == self_mntns_st.st_ino && st.st_dev == self_mntns_st.st_dev)
++                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace");
++
+         /* One day, when bind mounting /proc/self/fd/n works across
+          * namespace boundaries we should rework this logic to make
+          * use of it... */
+@@ -877,27 +897,15 @@ int bind_mount_in_namespace(
+                 goto finish;
+         }
+ 
+-        r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child);
++        r = namespace_fork("(sd-bindmnt)", "(sd-bindmnt-inner)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
++                           -1, mntns_fd, -1, -1, root_fd, &child);
+         if (r < 0)
+                 goto finish;
+         if (r == 0) {
+-                const char *mount_inside, *q;
+-                int mntfd;
++                const char *mount_inside;
+ 
+                 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+ 
+-                q = procfs_file_alloca(target, "ns/mnt");
+-                mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+-                if (mntfd < 0) {
+-                        r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
+-                        goto child_fail;
+-                }
+-
+-                if (setns(mntfd, CLONE_NEWNS) < 0) {
+-                        r = log_error_errno(errno, "Failed to join namespace of leader: %m");
+-                        goto child_fail;
+-                }
+-
+                 if (make_file_or_directory) {
+                         (void) mkdir_parents(dest, 0755);
+                         (void) make_mount_point_inode_from_stat(&st, dest, 0700);
diff --git a/debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch b/debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch
new file mode 100644
index 00000000..5ab54bf5
--- /dev/null
+++ b/debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch
@@ -0,0 +1,264 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Tue, 30 Nov 2021 22:29:05 +0100
+Subject: shared/rm-rf: loop over nested directories instead of instead of
+ recursing
+
+To remove directory structures, we need to remove the innermost items first,
+and then recursively remove higher-level directories. We would recursively
+descend into directories and invoke rm_rf_children and rm_rm_children_inner.
+This is problematic when too many directories are nested.
+
+Instead, let's create a "TODO" queue. In the the queue, for each level we
+hold the DIR* object we were working on, and the name of the directory. This
+allows us to leave a partially-processed directory, and restart the removal
+loop one level down. When done with the inner directory, we use the name to
+unlinkat() it from the parent, and proceed with the removal of other items.
+
+Because the nesting is increased by one level, it is best to view this patch
+with -b/--ignore-space-change.
+
+This fixes CVE-2021-3997, https://bugzilla.redhat.com/show_bug.cgi?id=2024639.
+The issue was reported and patches reviewed by Qualys Team.
+Mauro Matteo Cascella and Riccardo Schirone from Red Hat handled the disclosure.
+
+(cherry picked from commit 5b1cf7a9be37e20133c0208005274ce4a5b5c6a1)
+(cherry picked from commit 911516e1614e435755814ada5fc6064fa107a105)
+(cherry picked from commit 6a28f8b55904c818b25e4db2e1511faac79fd471)
+(cherry picked from commit c752f27b7647c99b4a17477c99d84fd8c950ddf0)
+(cherry picked from commit 921810ea23357988ce67f49190f43abef1788a9c)
+---
+ src/basic/rm-rf.c | 160 ++++++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 113 insertions(+), 47 deletions(-)
+
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index 2901307..77ffed9 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -115,12 +115,13 @@ static int fstatat_harder(
+         return 0;
+ }
+ 
+-static int rm_rf_children_inner(
++static int rm_rf_inner_child(
+                 int fd,
+                 const char *fname,
+                 int is_dir,
+                 RemoveFlags flags,
+-                const struct stat *root_dev) {
++                const struct stat *root_dev,
++                bool allow_recursion) {
+ 
+         struct stat st;
+         int r, q = 0;
+@@ -140,9 +141,7 @@ static int rm_rf_children_inner(
+         }
+ 
+         if (is_dir) {
+-                _cleanup_close_ int subdir_fd = -1;
+-
+-                /* if root_dev is set, remove subdirectories only if device is same */
++                /* If root_dev is set, remove subdirectories only if device is same */
+                 if (root_dev && st.st_dev != root_dev->st_dev)
+                         return 0;
+ 
+@@ -154,7 +153,6 @@ static int rm_rf_children_inner(
+                         return 0;
+ 
+                 if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
+-
+                         /* This could be a subvolume, try to remove it */
+ 
+                         r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
+@@ -168,13 +166,16 @@ static int rm_rf_children_inner(
+                                 return 1;
+                 }
+ 
+-                subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
++                if (!allow_recursion)
++                        return -EISDIR;
++
++                int subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                 if (subdir_fd < 0)
+                         return -errno;
+ 
+                 /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type
+                  * again for each directory */
+-                q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
++                q = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
+ 
+         } else if (flags & REMOVE_ONLY_DIRECTORIES)
+                 return 0;
+@@ -187,63 +188,128 @@ static int rm_rf_children_inner(
+         return 1;
+ }
+ 
++typedef struct TodoEntry {
++        DIR *dir;         /* A directory that we were operating on. */
++        char *dirname;    /* The filename of that directory itself. */
++} TodoEntry;
++
++static void free_todo_entries(TodoEntry **todos) {
++        for (TodoEntry *x = *todos; x && x->dir; x++) {
++                closedir(x->dir);
++                free(x->dirname);
++        }
++
++        freep(todos);
++}
++
+ int rm_rf_children(
+                 int fd,
+                 RemoveFlags flags,
+                 const struct stat *root_dev) {
+ 
+-        _cleanup_closedir_ DIR *d = NULL;
+-        struct dirent *de;
++        _cleanup_(free_todo_entries) TodoEntry *todos = NULL;
++        size_t n_todo = 0, n_todo_alloc = 0;
++        _cleanup_free_ char *dirname = NULL; /* Set when we are recursing and want to delete ourselves */
+         int ret = 0, r;
+ 
+-        assert(fd >= 0);
++        /* Return the first error we run into, but nevertheless try to go on.
++         * The passed fd is closed in all cases, including on failure. */
++
++        for (;;) {  /* This loop corresponds to the directory nesting level. */
++                _cleanup_closedir_ DIR *d = NULL;
++
++                if (n_todo > 0) {
++                        /* We know that we are in recursion here, because n_todo is set.
++                         * We need to remove the inner directory we were operating on. */
++                        assert(dirname);
++                        r = unlinkat_harder(dirfd(todos[n_todo-1].dir), dirname, AT_REMOVEDIR, flags);
++                        if (r < 0 && r != -ENOENT && ret == 0)
++                                ret = r;
++                        dirname = mfree(dirname);
++
++                        /* And now let's back out one level up */
++                        n_todo --;
++                        d = TAKE_PTR(todos[n_todo].dir);
++                        dirname = TAKE_PTR(todos[n_todo].dirname);
++
++                        assert(d);
++                        fd = dirfd(d); /* Retrieve the file descriptor from the DIR object */
++                        assert(fd >= 0);
++                } else {
++        next_fd:
++                        assert(fd >= 0);
++                        d = fdopendir(fd);
++                        if (!d) {
++                                safe_close(fd);
++                                return -errno;
++                        }
++                        fd = dirfd(d); /* We donated the fd to fdopendir(). Let's make sure we sure we have
++                                        * the right descriptor even if it were to internally invalidate the
++                                        * one we passed. */
++
++                        if (!(flags & REMOVE_PHYSICAL)) {
++                                struct statfs sfs;
++
++                                if (fstatfs(fd, &sfs) < 0)
++                                        return -errno;
++
++                                if (is_physical_fs(&sfs)) {
++                                        /* We refuse to clean physical file systems with this call, unless
++                                         * explicitly requested. This is extra paranoia just to be sure we
++                                         * never ever remove non-state data. */
++
++                                        _cleanup_free_ char *path = NULL;
++
++                                        (void) fd_get_path(fd, &path);
++                                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
++                                                               "Attempted to remove disk file system under \"%s\", and we can't allow that.",
++                                                               strna(path));
++                                }
++                        }
++                }
+ 
+-        /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
+-         * fd, in all cases, including on failure. */
++                struct dirent *de;
++                FOREACH_DIRENT_ALL(de, d, return -errno) {
++                        int is_dir;
+ 
+-        d = fdopendir(fd);
+-        if (!d) {
+-                safe_close(fd);
+-                return -errno;
+-        }
++                        if (dot_or_dot_dot(de->d_name))
++                                continue;
+ 
+-        if (!(flags & REMOVE_PHYSICAL)) {
+-                struct statfs sfs;
++                        is_dir = de->d_type == DT_UNKNOWN ? -1 : de->d_type == DT_DIR;
+ 
+-                if (fstatfs(dirfd(d), &sfs) < 0)
+-                        return -errno;
++                        r = rm_rf_inner_child(fd, de->d_name, is_dir, flags, root_dev, false);
++                        if (r == -EISDIR) {
++                                /* Push the current working state onto the todo list */
+ 
+-                if (is_physical_fs(&sfs)) {
+-                        /* We refuse to clean physical file systems with this call, unless explicitly
+-                         * requested. This is extra paranoia just to be sure we never ever remove non-state
+-                         * data. */
++                                 if (!GREEDY_REALLOC0(todos, n_todo_alloc, n_todo + 2))
++                                         return log_oom();
+ 
+-                        _cleanup_free_ char *path = NULL;
++                                 _cleanup_free_ char *newdirname = strdup(de->d_name);
++                                 if (!newdirname)
++                                         return log_oom();
+ 
+-                        (void) fd_get_path(fd, &path);
+-                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+-                                               "Attempted to remove disk file system under \"%s\", and we can't allow that.",
+-                                               strna(path));
+-                }
+-        }
++                                 int newfd = openat(fd, de->d_name,
++                                                    O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
++                                 if (newfd >= 0) {
++                                         todos[n_todo++] = (TodoEntry) { TAKE_PTR(d), TAKE_PTR(dirname) };
++                                         fd = newfd;
++                                         dirname = TAKE_PTR(newdirname);
+ 
+-        FOREACH_DIRENT_ALL(de, d, return -errno) {
+-                int is_dir;
++                                         goto next_fd;
+ 
+-                if (dot_or_dot_dot(de->d_name))
+-                        continue;
++                                 } else if (errno != -ENOENT && ret == 0)
++                                         ret = -errno;
+ 
+-                is_dir =
+-                        de->d_type == DT_UNKNOWN ? -1 :
+-                        de->d_type == DT_DIR;
++                        } else if (r < 0 && r != -ENOENT && ret == 0)
++                                ret = r;
++                }
+ 
+-                r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev);
+-                if (r < 0 && r != -ENOENT && ret == 0)
+-                        ret = r;
+-        }
++                if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(fd) < 0 && ret >= 0)
++                        ret = -errno;
+ 
+-        if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0)
+-                ret = -errno;
++                if (n_todo == 0)
++                        break;
++        }
+ 
+         return ret;
+ }
+@@ -336,5 +402,5 @@ int rm_rf_child(int fd, const char *name, RemoveFlags flags) {
+         if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
+                 return -EINVAL;
+ 
+-        return rm_rf_children_inner(fd, name, -1, flags, NULL);
++        return rm_rf_inner_child(fd, name, -1, flags, NULL, true);
+ }
diff --git a/debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch b/debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch
new file mode 100644
index 00000000..6f1d1c05
--- /dev/null
+++ b/debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch
@@ -0,0 +1,99 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Tue, 23 Nov 2021 16:56:42 +0100
+Subject: shared/rm_rf: refactor rm_rf() to shorten code a bit
+
+(cherry picked from commit 84ced330020c0bae57bd4628f1f44eec91304e69)
+(cherry picked from commit 664529efa9431edc043126013ea54e6c399ae2d3)
+(cherry picked from commit 811b137d6137cc3e8932599e6ef9254ba43ff5eb)
+(cherry picked from commit 39a53d4f1445a8981efd0adcc1734dfad46647c5)
+(cherry picked from commit aaad978868bd6ac84d463a94357ddcbc43b24248)
+---
+ src/basic/rm-rf.c | 54 ++++++++++++++++++++++++------------------------------
+ 1 file changed, 24 insertions(+), 30 deletions(-)
+
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index 343a097..2901307 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -249,7 +249,7 @@ int rm_rf_children(
+ }
+ 
+ int rm_rf(const char *path, RemoveFlags flags) {
+-        int fd, r;
++        int fd, r, q = 0;
+ 
+         assert(path);
+ 
+@@ -281,49 +281,43 @@ int rm_rf(const char *path, RemoveFlags flags) {
+         }
+ 
+         fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+-        if (fd < 0) {
++        if (fd >= 0) {
++                /* We have a dir */
++                r = rm_rf_children(fd, flags, NULL);
++
++                if (FLAGS_SET(flags, REMOVE_ROOT) && rmdir(path) < 0)
++                        q = -errno;
++        } else {
+                 if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+                         return 0;
+ 
+                 if (!IN_SET(errno, ENOTDIR, ELOOP))
+                         return -errno;
+ 
+-                if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES))
++                if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES) || !FLAGS_SET(flags, REMOVE_ROOT))
+                         return 0;
+ 
+-                if (FLAGS_SET(flags, REMOVE_ROOT)) {
+-
+-                        if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
+-                                struct statfs s;
+-
+-                                if (statfs(path, &s) < 0)
+-                                        return -errno;
+-                                if (is_physical_fs(&s))
+-                                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+-                                                               "Attempted to remove files from a disk file system under \"%s\", refusing.",
+-                                                               path);
+-                        }
+-
+-                        if (unlink(path) < 0) {
+-                                if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+-                                        return 0;
++                if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
++                        struct statfs s;
+ 
++                        if (statfs(path, &s) < 0)
+                                 return -errno;
+-                        }
++                        if (is_physical_fs(&s))
++                                return log_error_errno(SYNTHETIC_ERRNO(EPERM),
++                                                       "Attempted to remove files from a disk file system under \"%s\", refusing.",
++                                                       path);
+                 }
+ 
+-                return 0;
++                r = 0;
++                if (unlink(path) < 0)
++                        q = -errno;
+         }
+ 
+-        r = rm_rf_children(fd, flags, NULL);
+-
+-        if (FLAGS_SET(flags, REMOVE_ROOT) &&
+-            rmdir(path) < 0 &&
+-            r >= 0 &&
+-            (!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT))
+-                r = -errno;
+-
+-        return r;
++        if (r < 0)
++                return r;
++        if (q < 0 && (q != -ENOENT || !FLAGS_SET(flags, REMOVE_MISSING_OK)))
++                return q;
++        return 0;
+ }
+ 
+ int rm_rf_child(int fd, const char *name, RemoveFlags flags) {
diff --git a/debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch b/debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch
new file mode 100644
index 00000000..7a7f85a8
--- /dev/null
+++ b/debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch
@@ -0,0 +1,66 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Tue, 23 Nov 2021 15:55:45 +0100
+Subject: shared/rm_rf: refactor rm_rf_children_inner() to shorten code a bit
+
+(cherry picked from commit 3bac86abfa1b1720180840ffb9d06b3d54841c11)
+(cherry picked from commit 47741ff9eae6311a03e4d3d837128191826a4a3a)
+(cherry picked from commit 89395b63f04f1acc0db533c32637ea20379f97c0)
+(cherry picked from commit 3976f244990aa1210ebe018647f32ab060e1c3d3)
+(cherry picked from commit 988e43630bb7592947c75fe530a6f7dfebc00c4f)
+---
+ src/basic/rm-rf.c | 27 +++++++++------------------
+ 1 file changed, 9 insertions(+), 18 deletions(-)
+
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index a78aa4f..343a097 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -123,7 +123,7 @@ static int rm_rf_children_inner(
+                 const struct stat *root_dev) {
+ 
+         struct stat st;
+-        int r;
++        int r, q = 0;
+ 
+         assert(fd >= 0);
+         assert(fname);
+@@ -141,7 +141,6 @@ static int rm_rf_children_inner(
+ 
+         if (is_dir) {
+                 _cleanup_close_ int subdir_fd = -1;
+-                int q;
+ 
+                 /* if root_dev is set, remove subdirectories only if device is same */
+                 if (root_dev && st.st_dev != root_dev->st_dev)
+@@ -177,23 +176,15 @@ static int rm_rf_children_inner(
+                  * again for each directory */
+                 q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
+ 
+-                r = unlinkat_harder(fd, fname, AT_REMOVEDIR, flags);
+-                if (r < 0)
+-                        return r;
+-                if (q < 0)
+-                        return q;
+-
+-                return 1;
+-
+-        } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+-                r = unlinkat_harder(fd, fname, 0, flags);
+-                if (r < 0)
+-                        return r;
+-
+-                return 1;
+-        }
++        } else if (flags & REMOVE_ONLY_DIRECTORIES)
++                return 0;
+ 
+-        return 0;
++        r = unlinkat_harder(fd, fname, is_dir ? AT_REMOVEDIR : 0, flags);
++        if (r < 0)
++                return r;
++        if (q < 0)
++                return q;
++        return 1;
+ }
+ 
+ int rm_rf_children(
diff --git a/debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch b/debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch
new file mode 100644
index 00000000..a8877b30
--- /dev/null
+++ b/debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch
@@ -0,0 +1,27 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Tue, 23 Nov 2021 15:05:58 +0100
+Subject: tmpfiles: 'st' may have been used uninitialized
+
+(cherry picked from commit 160dadc0350c77d612aa9d5569f57d9bc84c3dca)
+(cherry picked from commit 7563de501246dccf5a9ea229933481aa1e7bd5c9)
+(cherry picked from commit f54b97b1d05052bfee824ecc03ae9f07f6c37be8)
+(cherry picked from commit ab927db9a7698ee1eceae14ecef7ab43ee3f104e)
+---
+ src/basic/rm-rf.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
+index cf671c2..a78aa4f 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -128,7 +128,9 @@ static int rm_rf_children_inner(
+         assert(fd >= 0);
+         assert(fname);
+ 
+-        if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
++        if (is_dir < 0 ||
++            root_dev ||
++            (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
+ 
+                 r = fstatat_harder(fd, fname, &st, AT_SYMLINK_NOFOLLOW, flags);
+                 if (r < 0)
diff --git a/debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch b/debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch
new file mode 100644
index 00000000..cabec71c
--- /dev/null
+++ b/debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch
@@ -0,0 +1,58 @@
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Sat, 20 Feb 2021 16:30:23 +0900
+Subject: udevadm-trigger: do not return immediately on EACCES
+
+Prompted by https://github.com/systemd/systemd/pull/18559.
+
+(cherry picked from commit 0e789e6d48046d43c50dd949a71ac56f1127bb96)
+---
+ src/udev/udevadm-trigger.c | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
+index 5c74184..da9b89a 100644
+--- a/src/udev/udevadm-trigger.c
++++ b/src/udev/udevadm-trigger.c
+@@ -45,13 +45,39 @@ static int exec_list(sd_device_enumerator *e, const char *action, Set **settle_s
+ 
+                 r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
+                 if (r < 0) {
++                        /* ENOENT may be returned when a device does not have /uevent or is already
++                         * removed. Hence, this is logged at debug level and ignored.
++                         *
++                         * ENODEV may be returned by some buggy device drivers e.g. /sys/devices/vio.
++                         * See,
++                         * https://github.com/systemd/systemd/issues/13652#issuecomment-535129791 and
++                         * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1845319.
++                         * So, this error is ignored, but logged at warning level to encourage people to
++                         * fix the driver.
++                         *
++                         * EROFS is returned when /sys is read only. In that case, all subsequent
++                         * writes will also fail, hence return immediately.
++                         *
++                         * EACCES or EPERM may be returned when this is invoked by non-priviledged user.
++                         * We do NOT return immediately, but continue operation and propagate the error.
++                         * Why? Some device can be owned by a user, e.g., network devices configured in
++                         * a network namespace. See, https://github.com/systemd/systemd/pull/18559 and
++                         * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebb4a4bf76f164457184a3f43ebc1552416bc823
++                         *
++                         * All other errors are logged at error level, but let's continue the operation,
++                         * and propagate the error.
++                         */
++
+                         bool ignore = IN_SET(r, -ENOENT, -ENODEV);
++                        int level =
++                                r == -ENOENT ? LOG_DEBUG :
++                                r == -ENODEV ? LOG_WARNING : LOG_ERR;
+ 
+-                        log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
++                        log_full_errno(level, r,
+                                        "Failed to write '%s' to '%s'%s: %m",
+                                        action, filename, ignore ? ", ignoring" : "");
+-                        if (IN_SET(r, -EACCES, -EROFS))
+-                                /* Inovoked by unpriviledged user, or read only filesystem. Return earlier. */
++
++                        if (r == -EROFS)
+                                 return r;
+                         if (ret == 0 && !ignore)
+                                 ret = r;
diff --git a/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch b/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch
index f351607f..c28266b4 100644
--- a/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch
+++ b/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch
@@ -1,19 +1,21 @@
 From: Lennart Poettering <lennart@poettering.net>
 Date: Tue, 1 Jun 2021 19:43:55 +0200
-Subject: unit-name: generate a clear error code when converting an overly long
- fs path to a unit name
-Origin: https://github.com/systemd/systemd/commit/9d5acfab20c5f1177d877d0bec18063c0a6c5929
+Subject: unit-name: generate a clear error code when converting an overly
+ long fs path to a unit name
 
-[Salvatore Bonaccorso: Backport to 247.3 for context changes in
-src/test/test-unit-name.c]
+(cherry picked from commit 9d5acfab20c5f1177d877d0bec18063c0a6c5929)
+(cherry picked from commit 1579dce2c2a162bb09afb9a8a46fd4f7e8fbf1d5)
+(cherry picked from commit 0488b743e9c6ab1e885933eebda4ba9232003a2a)
 ---
  src/basic/unit-name.c     | 6 ++++++
  src/test/test-unit-name.c | 4 ++--
  2 files changed, 8 insertions(+), 2 deletions(-)
 
+diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
+index c1529bb..5f595af 100644
 --- a/src/basic/unit-name.c
 +++ b/src/basic/unit-name.c
-@@ -528,6 +528,9 @@ int unit_name_from_path(const char *path
+@@ -528,6 +528,9 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) {
          if (!s)
                  return -ENOMEM;
  
@@ -23,7 +25,7 @@ src/test/test-unit-name.c]
          /* Refuse this if this got too long or for some other reason didn't result in a valid name */
          if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
                  return -EINVAL;
-@@ -559,6 +562,9 @@ int unit_name_from_path_instance(const c
+@@ -559,6 +562,9 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha
          if (!s)
                  return -ENOMEM;
  
@@ -33,9 +35,11 @@ src/test/test-unit-name.c]
          /* Refuse this if this got too long or for some other reason didn't result in a valid name */
          if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE))
                  return -EINVAL;
+diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
+index ece78aa..c0b7971 100644
 --- a/src/test/test-unit-name.c
 +++ b/src/test/test-unit-name.c
-@@ -130,7 +130,7 @@ static void test_unit_name_from_path(voi
+@@ -130,7 +130,7 @@ static void test_unit_name_from_path(void) {
          test_unit_name_from_path_one("///", ".mount", "-.mount", 0);
          test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL);
          test_unit_name_from_path_one("/foo/./bar", ".mount", NULL, -EINVAL);
@@ -44,7 +48,7 @@ src/test/test-unit-name.c]
  }
  
  static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) {
-@@ -160,7 +160,7 @@ static void test_unit_name_from_path_ins
+@@ -160,7 +160,7 @@ static void test_unit_name_from_path_instance(void) {
          test_unit_name_from_path_instance_one("waldo", "..", ".mount", NULL, -EINVAL);
          test_unit_name_from_path_instance_one("waldo", "/foo", ".waldi", NULL, -EINVAL);
          test_unit_name_from_path_instance_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0);
diff --git a/debian/tests/control b/debian/tests/control
index f4887ef6..9c5f2827 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,5 +1,6 @@
 Tests: timedated, hostnamed, localed-locale, localed-x11-keymap
 Depends: systemd,
+  systemd-timesyncd,
   libpam-systemd,
   libnss-systemd,
   acl,
-- 
GitLab