Bug#1003948: bullseye-pu: package systemd/247.3-7

Michael Biebl biebl at debian.org
Tue Jan 18 13:46:06 GMT 2022


Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian.org at packages.debian.org
Usertags: pu
X-Debbugs-Cc: pkg-systemd-maintainers at lists.alioth.debian.org, kibi at debian.org, debian-boot at debian.org, ftpmaster at ftp-master.debian.org

Hi release team,

I'd like to make a stable upload for systemd, fixing various issues
including a CVE.

What follows is an annotated list of changes. A full debdiff is also
attached for your convenience.

As usual, I CCed debian-boot i.e. kibi for his ack regarding d-i.


  * Switch debian-branch to debian/bullseye

https://salsa.debian.org/biebl/systemd/-/commit/72099a57e48da2d34857828eba016e5819d82fbb
https://salsa.debian.org/biebl/systemd/-/commit/6cd8fde618a3e86d9191db252cc2e38fd68e61fc

I replaced some patches from 247.3-6 with (upstream) cherry-picks. No
actual code changes. This was just done to avoid patch noise in the
other patches that follow.

  * udevadm-trigger: do not return immediately on EACCES.
    Fixes a regression when using systemd-networkd in an unprivileged LXD
    container. (Closes: #997006)

https://salsa.debian.org/biebl/systemd/-/commit/6c4f3c69d753edc8ca963c9f6f86f76bd30275c6

Straightforward cherry-pick from upstream. Confirmed by the bug submitter
that it fixes the issue in #997006

Touches udev code but I don't expect any effect on d-i.

  * 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)

https://salsa.debian.org/biebl/systemd/-/commit/e9ec5186a719afefbff7bfd9b7514482ad896ff3

While this was fixed differently in systemd main (and the fix landed in
v250 eventually), the changes were a bit too heavy-weight imho for a
backport. So I decided to revert this upstream change.
The offending commits were part of v247, so we restored the previous
behaviour of v246 and earlier.

  * shared/rm-rf: loop over nested directories instead of recursing.
    Fixes uncontrolled recursion in systemd-tmpfiles.
    (CVE-2021-3997, Closes: #1003467)

https://salsa.debian.org/biebl/systemd/-/commit/2a546b515cf2835968364dfd0d3e82bd1b589d94

I talked to Salvatore from the security team about this and we concluded
that we want to fix this via a stable upload.
The diff is rather large, as it cherry-picks quite a few preparatory
commits to make the actual cherry-pick of the fix easier.

The fix itself is also in unstable/testing and I confirmed that with
these changes applied, systemd-tmpfiles no longer segfaults with the
reproducer provided in https://www.openwall.com/lists/oss-security/2022/01/10/2


  * 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)

This one is probably the trickiest (and possibly also the simplest)
change. It simply breaks a dependency loop between systemd and
systemd-timesyncd resulting in a more predictable upgrade sequence which
in turn ensures that modifications of systemd-timesyncd's conffiles are
preserved on upgrades.

As systemd is installed during the initial bootstrap phase, where
Recommends are not considered, we would end up with no systemd-timesycnd
being installed on fresh installations.
To avoid that, we'd like to bump the priority of systemd-timesyncd to
standard in stable (so it is installed via the standard task).
The same change already happend in unstable/testing, but changing the
priority of a package in stable is rather unusual, so there might be
issues that need to be considered here.

I've CCed the ftp-master team for their input and will also file a
corresponding override bug report.


Thanks for all your work!

Regards,
Michael
-------------- next part --------------
diff --git a/debian/changelog b/debian/changelog
index 4659f3b..ff1e137 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,27 @@
+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)
+
+ -- Michael Biebl <biebl at debian.org>  Tue, 18 Jan 2022 13:51:41 +0100
+
 systemd (247.3-6) unstable; urgency=high
 
   * Non-maintainer upload (acked by maintainers)
diff --git a/debian/control b/debian/control
index be7c47b..c0cc0dc 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 fb40ad3..a34c597 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-unit-name-adjust-comments.patch b/debian/patches/basic-unit-name-adjust-comments.patch
index d46e0c9..d83b1d7 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 at in.waw.pl>
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek at 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 0faa7d1..b080d25 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 at in.waw.pl>
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek at 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 0000000..0dffcf3
--- /dev/null
+++ b/debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch
@@ -0,0 +1,106 @@
+From: Lennart Poettering <lennart at 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 0000000..ed90a10
--- /dev/null
+++ b/debian/patches/debian/Revert-udev-fix-memleak.patch
@@ -0,0 +1,30 @@
+From: Michael Biebl <biebl at 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 0000000..e3f1c64
--- /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 at 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 0000000..36bdbb5
--- /dev/null
+++ b/debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch
@@ -0,0 +1,163 @@
+From: Michael Biebl <biebl at 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/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 0000000..67d959c
--- /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 at 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 0000000..66a1ef5
--- /dev/null
+++ b/debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch
@@ -0,0 +1,39 @@
+From: Lennart Poettering <lennart at 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 0000000..8692c2f
--- /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 at 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 07fb32d..826c1d0 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -10,6 +10,15 @@ 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
 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 +41,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-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 0000000..5ab54bf
--- /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 at 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 0000000..6f1d1c0
--- /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 at 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 0000000..7a7f85a
--- /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 at 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 0000000..a8877b3
--- /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 at 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 0000000..cabec71
--- /dev/null
+++ b/debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch
@@ -0,0 +1,58 @@
+From: Yu Watanabe <watanabe.yu+github at 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 f351607..c28266b 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 at 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);


More information about the Pkg-systemd-maintainers mailing list