summaryrefslogtreecommitdiffstats
path: root/debian/patches
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 13:00:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 13:00:48 +0000
commitf542925b701989ba6eed7b08b5226d4021b9b85f (patch)
tree57e14731f21a6d663326d30b7b88736e9d51c420 /debian/patches
parentAdding upstream version 247.3. (diff)
downloadsystemd-f542925b701989ba6eed7b08b5226d4021b9b85f.tar.xz
systemd-f542925b701989ba6eed7b08b5226d4021b9b85f.zip
Adding debian version 247.3-7+deb11u4.debian/247.3-7+deb11u4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--debian/patches/Add-helper-for-case-independent-string-equality-checks.patch24
-rw-r--r--debian/patches/Always-free-deserialized_subscribed-on-reload.patch25
-rw-r--r--debian/patches/Drop-bundled-copy-of-linux-if_arp.h.patch219
-rw-r--r--debian/patches/LoadCredentials-do-not-assert-on-invalid-syntax.patch34
-rw-r--r--debian/patches/Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch52
-rw-r--r--debian/patches/analyze-slightly-reword-PrivateTmp-message.patch26
-rw-r--r--debian/patches/ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch37
-rw-r--r--debian/patches/basic-add-make_mount_point_inode-helper.patch239
-rw-r--r--debian/patches/basic-unit-name-adjust-comments.patch36
-rw-r--r--debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch65
-rw-r--r--debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch106
-rw-r--r--debian/patches/core-fix-mtime-calculation-of-dropin-files.patch100
-rw-r--r--debian/patches/coredump-do-not-allow-user-to-access-coredumps-with-chang.patch388
-rw-r--r--debian/patches/debian/Add-env-variable-for-machine-ID-path.patch77
-rw-r--r--debian/patches/debian/Add-support-for-TuxOnIce-hibernation.patch30
-rw-r--r--debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch21
-rw-r--r--debian/patches/debian/Don-t-enable-audit-by-default.patch53
-rw-r--r--debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch74
-rw-r--r--debian/patches/debian/Drop-seccomp-system-call-filter-for-udev.patch31
-rw-r--r--debian/patches/debian/Keep-journal-files-compatible-with-older-versions.patch69
-rw-r--r--debian/patches/debian/Let-graphical-session-pre.target-be-manually-started.patch22
-rw-r--r--debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch42
-rw-r--r--debian/patches/debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch68
-rw-r--r--debian/patches/debian/Only-start-logind-if-dbus-is-installed.patch24
-rw-r--r--debian/patches/debian/Re-enable-journal-forwarding-to-syslog.patch56
-rw-r--r--debian/patches/debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch37
-rw-r--r--debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch46
-rw-r--r--debian/patches/debian/Revert-udev-fix-memleak.patch30
-rw-r--r--debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch47
-rw-r--r--debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch163
-rw-r--r--debian/patches/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch57
-rw-r--r--debian/patches/debian/Use-Debian-specific-config-files.patch459
-rw-r--r--debian/patches/debian/deny-list-upstream-test-02-ppc64el.patch17
-rw-r--r--debian/patches/debian/deny-list-upstream-test-25.patch17
-rw-r--r--debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch1065
-rw-r--r--debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch52
-rw-r--r--debian/patches/debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch131
-rw-r--r--debian/patches/debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch25
-rw-r--r--debian/patches/localed-Run-locale-gen-if-available-to-generate-missing-l.patch448
-rw-r--r--debian/patches/logind-fix-getting-property-OnExternalPower-via-D-Bus.patch36
-rw-r--r--debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch28
-rw-r--r--debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch499
-rw-r--r--debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch105
-rw-r--r--debian/patches/machined-varlink-fix-double-free.patch22
-rw-r--r--debian/patches/network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch86
-rw-r--r--debian/patches/pkg-config-make-prefix-overridable-again.patch75
-rw-r--r--debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch128
-rw-r--r--debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch39
-rw-r--r--debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch320
-rw-r--r--debian/patches/rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch41
-rw-r--r--debian/patches/series66
-rw-r--r--debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch55
-rw-r--r--debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch105
-rw-r--r--debian/patches/shared-mount-util-use-namespace_fork-utils.patch92
-rw-r--r--debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch264
-rw-r--r--debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch99
-rw-r--r--debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch66
-rw-r--r--debian/patches/table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch167
-rw-r--r--debian/patches/time-util-fix-buffer-over-run.patch55
-rw-r--r--debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch27
-rw-r--r--debian/patches/udev-always-create-device-symlinks-for-USB-disks.patch111
-rw-r--r--debian/patches/udev-first-set-properties-based-on-usb-subsystem.patch34
-rw-r--r--debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch58
-rw-r--r--debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch59
-rw-r--r--debian/patches/virt-Fix-the-detection-for-Hyper-V-VMs.patch38
-rw-r--r--debian/patches/virt-Support-detection-for-ARM64-Hyper-V-guests.patch28
-rw-r--r--debian/patches/virt-detect-OpenStack-Nova-instance.patch21
67 files changed, 7236 insertions, 0 deletions
diff --git a/debian/patches/Add-helper-for-case-independent-string-equality-checks.patch b/debian/patches/Add-helper-for-case-independent-string-equality-checks.patch
new file mode 100644
index 0000000..8e69db9
--- /dev/null
+++ b/debian/patches/Add-helper-for-case-independent-string-equality-checks.patch
@@ -0,0 +1,24 @@
+From: Matthias Klumpp <matthias@tenstral.net>
+Date: Sat, 10 Oct 2020 04:40:23 +0200
+Subject: Add helper for case-independent string equality checks
+
+(cherry picked from commit bd47b0dac4a1ff6e686c99b9958693e86d44007b)
+---
+ src/basic/string-util.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/basic/string-util.h b/src/basic/string-util.h
+index fdd3ce7..6c99335 100644
+--- a/src/basic/string-util.h
++++ b/src/basic/string-util.h
+@@ -33,6 +33,10 @@ static inline bool streq_ptr(const char *a, const char *b) {
+ return strcmp_ptr(a, b) == 0;
+ }
+
++static inline bool strcaseeq_ptr(const char *a, const char *b) {
++ return strcasecmp_ptr(a, b) == 0;
++}
++
+ static inline char* strstr_ptr(const char *haystack, const char *needle) {
+ if (!haystack || !needle)
+ return NULL;
diff --git a/debian/patches/Always-free-deserialized_subscribed-on-reload.patch b/debian/patches/Always-free-deserialized_subscribed-on-reload.patch
new file mode 100644
index 0000000..f0f6129
--- /dev/null
+++ b/debian/patches/Always-free-deserialized_subscribed-on-reload.patch
@@ -0,0 +1,25 @@
+From: Ali Abdallah <ali.abdallah@suse.com>
+Date: Thu, 21 Jan 2021 07:37:21 +0100
+Subject: Always free deserialized_subscribed on reload
+
+Otherwise, it will keep consuming memory on systemctl daemon-reload.
+
+(cherry picked from commit 3deed59afdc2c18ecb76fe90b9bba0cd66045dfa)
+---
+ src/core/manager.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/core/manager.c b/src/core/manager.c
+index a1d6f7c..6858950 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -3842,6 +3842,9 @@ int manager_reload(Manager *m) {
+ /* Clean up runtime objects no longer referenced */
+ manager_vacuum(m);
+
++ /* Clean up deserialized tracked clients */
++ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
++
+ /* Consider the reload process complete now. */
+ assert(m->n_reloading > 0);
+ m->n_reloading--;
diff --git a/debian/patches/Drop-bundled-copy-of-linux-if_arp.h.patch b/debian/patches/Drop-bundled-copy-of-linux-if_arp.h.patch
new file mode 100644
index 0000000..83a6f2c
--- /dev/null
+++ b/debian/patches/Drop-bundled-copy-of-linux-if_arp.h.patch
@@ -0,0 +1,219 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Wed, 15 Sep 2021 16:33:05 +0200
+Subject: Drop bundled copy of linux/if_arp.h
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+As far as I can see, we use this to get a list of ARPHRD_* defines (used in
+particular for Type= in .link files). If we drop our copy, and build against
+old kernel headers, the user will have a shorter list of types available. This
+seems OK, and I don't think it's worth carrying our own version of this file
+just to have newest possible entries.
+
+7c5b9952c4f6e2b72f90edbe439982528b7cf223 recently updated this file, but we'd
+have to update it every time the kernel adds new entries. But if we look at
+the failure carefully:
+
+src/basic/arphrd-from-name.gperf:65:16: error: ‘ARPHRD_MCTP’ undeclared (first use in this function); did you mean ‘ARPHRD_FCPP’?
+ 65 | MCTP, ARPHRD_MCTP
+ | ^~
+ | ARPHRD_FCPP
+
+we see that the list we were generating was from the system headers, so it was
+only as good as the system headers anyway, without the newer entries in our
+bundled copy, if there were any. So let's make things simpler by always using
+system headers.
+
+And if somebody wants to fix things so that we always have the newest list,
+then we should just generate and store the converted list, not the full header.
+
+(cherry picked from commit e7f46ee3ae1cc66a94b293957721d68dc09d7449)
+---
+ src/basic/linux/if_arp.h | 164 -----------------------------------------------
+ src/basic/meson.build | 1 -
+ 2 files changed, 165 deletions(-)
+ delete mode 100644 src/basic/linux/if_arp.h
+
+diff --git a/src/basic/linux/if_arp.h b/src/basic/linux/if_arp.h
+deleted file mode 100644
+index c3cc5a9..0000000
+--- a/src/basic/linux/if_arp.h
++++ /dev/null
+@@ -1,164 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+-/*
+- * INET An implementation of the TCP/IP protocol suite for the LINUX
+- * operating system. INET is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * Global definitions for the ARP (RFC 826) protocol.
+- *
+- * Version: @(#)if_arp.h 1.0.1 04/16/93
+- *
+- * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
+- * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source.
+- * Ross Biro
+- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+- * Florian La Roche,
+- * Jonathan Layes <layes@loran.com>
+- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> ARPHRD_HWX25
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version
+- * 2 of the License, or (at your option) any later version.
+- */
+-#ifndef _UAPI_LINUX_IF_ARP_H
+-#define _UAPI_LINUX_IF_ARP_H
+-
+-#include <linux/netdevice.h>
+-
+-/* ARP protocol HARDWARE identifiers. */
+-#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */
+-#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
+-#define ARPHRD_EETHER 2 /* Experimental Ethernet */
+-#define ARPHRD_AX25 3 /* AX.25 Level 2 */
+-#define ARPHRD_PRONET 4 /* PROnet token ring */
+-#define ARPHRD_CHAOS 5 /* Chaosnet */
+-#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */
+-#define ARPHRD_ARCNET 7 /* ARCnet */
+-#define ARPHRD_APPLETLK 8 /* APPLEtalk */
+-#define ARPHRD_DLCI 15 /* Frame Relay DLCI */
+-#define ARPHRD_ATM 19 /* ATM */
+-#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */
+-#define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */
+-#define ARPHRD_EUI64 27 /* EUI-64 */
+-#define ARPHRD_INFINIBAND 32 /* InfiniBand */
+-
+-/* Dummy types for non ARP hardware */
+-#define ARPHRD_SLIP 256
+-#define ARPHRD_CSLIP 257
+-#define ARPHRD_SLIP6 258
+-#define ARPHRD_CSLIP6 259
+-#define ARPHRD_RSRVD 260 /* Notional KISS type */
+-#define ARPHRD_ADAPT 264
+-#define ARPHRD_ROSE 270
+-#define ARPHRD_X25 271 /* CCITT X.25 */
+-#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
+-#define ARPHRD_CAN 280 /* Controller Area Network */
+-#define ARPHRD_PPP 512
+-#define ARPHRD_CISCO 513 /* Cisco HDLC */
+-#define ARPHRD_HDLC ARPHRD_CISCO
+-#define ARPHRD_LAPB 516 /* LAPB */
+-#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */
+-#define ARPHRD_RAWHDLC 518 /* Raw HDLC */
+-#define ARPHRD_RAWIP 519 /* Raw IP */
+-
+-#define ARPHRD_TUNNEL 768 /* IPIP tunnel */
+-#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */
+-#define ARPHRD_FRAD 770 /* Frame Relay Access Device */
+-#define ARPHRD_SKIP 771 /* SKIP vif */
+-#define ARPHRD_LOOPBACK 772 /* Loopback device */
+-#define ARPHRD_LOCALTLK 773 /* Localtalk device */
+-#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */
+-#define ARPHRD_BIF 775 /* AP1000 BIF */
+-#define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4 */
+-#define ARPHRD_IPDDP 777 /* IP over DDP tunneller */
+-#define ARPHRD_IPGRE 778 /* GRE over IP */
+-#define ARPHRD_PIMREG 779 /* PIMSM register interface */
+-#define ARPHRD_HIPPI 780 /* High Performance Parallel Interface */
+-#define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */
+-#define ARPHRD_ECONET 782 /* Acorn Econet */
+-#define ARPHRD_IRDA 783 /* Linux-IrDA */
+-/* ARP works differently on different FC media .. so */
+-#define ARPHRD_FCPP 784 /* Point to point fibrechannel */
+-#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */
+-#define ARPHRD_FCPL 786 /* Fibrechannel public loop */
+-#define ARPHRD_FCFABRIC 787 /* Fibrechannel fabric */
+- /* 787->799 reserved for fibrechannel media types */
+-#define ARPHRD_IEEE802_TR 800 /* Magic type ident for TR */
+-#define ARPHRD_IEEE80211 801 /* IEEE 802.11 */
+-#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */
+-#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */
+-#define ARPHRD_IEEE802154 804
+-#define ARPHRD_IEEE802154_MONITOR 805 /* IEEE 802.15.4 network monitor */
+-
+-#define ARPHRD_PHONET 820 /* PhoNet media type */
+-#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
+-#define ARPHRD_CAIF 822 /* CAIF media type */
+-#define ARPHRD_IP6GRE 823 /* GRE over IPv6 */
+-#define ARPHRD_NETLINK 824 /* Netlink header */
+-#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
+-#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */
+-
+-#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
+-#define ARPHRD_NONE 0xFFFE /* zero header length */
+-
+-/* ARP protocol opcodes. */
+-#define ARPOP_REQUEST 1 /* ARP request */
+-#define ARPOP_REPLY 2 /* ARP reply */
+-#define ARPOP_RREQUEST 3 /* RARP request */
+-#define ARPOP_RREPLY 4 /* RARP reply */
+-#define ARPOP_InREQUEST 8 /* InARP request */
+-#define ARPOP_InREPLY 9 /* InARP reply */
+-#define ARPOP_NAK 10 /* (ATM)ARP NAK */
+-
+-
+-/* ARP ioctl request. */
+-struct arpreq {
+- struct sockaddr arp_pa; /* protocol address */
+- struct sockaddr arp_ha; /* hardware address */
+- int arp_flags; /* flags */
+- struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
+- char arp_dev[IFNAMSIZ];
+-};
+-
+-struct arpreq_old {
+- struct sockaddr arp_pa; /* protocol address */
+- struct sockaddr arp_ha; /* hardware address */
+- int arp_flags; /* flags */
+- struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
+-};
+-
+-/* ARP Flag values. */
+-#define ATF_COM 0x02 /* completed entry (ha valid) */
+-#define ATF_PERM 0x04 /* permanent entry */
+-#define ATF_PUBL 0x08 /* publish entry */
+-#define ATF_USETRAILERS 0x10 /* has requested trailers */
+-#define ATF_NETMASK 0x20 /* want to use a netmask (only
+- for proxy entries) */
+-#define ATF_DONTPUB 0x40 /* don't answer this addresses */
+-
+-/*
+- * This structure defines an ethernet arp header.
+- */
+-
+-struct arphdr {
+- __be16 ar_hrd; /* format of hardware address */
+- __be16 ar_pro; /* format of protocol address */
+- unsigned char ar_hln; /* length of hardware address */
+- unsigned char ar_pln; /* length of protocol address */
+- __be16 ar_op; /* ARP opcode (command) */
+-
+-#if 0
+- /*
+- * Ethernet looks like this : This bit is variable sized however...
+- */
+- unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+- unsigned char ar_sip[4]; /* sender IP address */
+- unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+- unsigned char ar_tip[4]; /* target IP address */
+-#endif
+-
+-};
+-
+-
+-#endif /* _UAPI_LINUX_IF_ARP_H */
+diff --git a/src/basic/meson.build b/src/basic/meson.build
+index 1183ea8..2c13cf4 100644
+--- a/src/basic/meson.build
++++ b/src/basic/meson.build
+@@ -98,7 +98,6 @@ basic_sources = files('''
+ linux/hdlc/ioctl.h
+ linux/if.h
+ linux/if_addr.h
+- linux/if_arp.h
+ linux/if_bonding.h
+ linux/if_bridge.h
+ linux/if_ether.h
diff --git a/debian/patches/LoadCredentials-do-not-assert-on-invalid-syntax.patch b/debian/patches/LoadCredentials-do-not-assert-on-invalid-syntax.patch
new file mode 100644
index 0000000..c9e3500
--- /dev/null
+++ b/debian/patches/LoadCredentials-do-not-assert-on-invalid-syntax.patch
@@ -0,0 +1,34 @@
+From: Luca Boccassi <luca.boccassi@microsoft.com>
+Date: Thu, 1 Apr 2021 22:18:29 +0100
+Subject: LoadCredentials: do not assert on invalid syntax
+
+LoadCredentials=foo causes an assertion to be triggered, as we
+are not checking that the rvalue's right hand side part is non-empty
+before using it in unit_full_printf.
+
+Fixes #19178
+
+# printf [Service]nLoadCredential=passwd.hashed-password.rootn > hello.service
+# systemd-analyze verify ./hello.service
+...
+Assertion 'format' failed at src/core/unit-printf.c:232, function unit_full_printf(). Aborting.
+Aborted (core dumped)
+
+(cherry picked from commit f7a6f1226e800f7695c2073675523062ea697aa4)
+---
+ src/core/load-fragment.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
+index 4964249..5b66fb1 100644
+--- a/src/core/load-fragment.c
++++ b/src/core/load-fragment.c
+@@ -4569,7 +4569,7 @@ int config_parse_load_credential(
+ r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r == -ENOMEM)
+ return log_oom();
+- if (r <= 0) {
++ if (r <= 0 || isempty(p)) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
diff --git a/debian/patches/Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch b/debian/patches/Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch
new file mode 100644
index 0000000..fa14d0d
--- /dev/null
+++ b/debian/patches/Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch
@@ -0,0 +1,52 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Wed, 10 Mar 2021 10:17:23 +0100
+Subject: Revert "udev: do not execute hwdb builtin import twice or thrice"
+
+This reverts commit 876c75fe870846b09b54423a6b719d80bc879b27.
+
+The patch seems to cause usb devices to get some attributes set from the parent
+PCI device. 'hwdb' builtin has support for breaking iteration upwards on usb
+devices. But when '--subsystem=foo' is specified, iteration is continued. I'm
+sure it *could* be figured out, but it seems hard to get all the combinations
+correct. So let's revert to functional status quo ante, even if does the lookup
+more than once unnecessarily.
+
+Fixes #18125.
+
+(cherry picked from commit 451ba55fecd8b494add2001b3ca3c1915c8fd655)
+---
+ rules.d/50-udev-default.rules.in | 3 +--
+ rules.d/60-serial.rules | 5 ++---
+ 2 files changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/rules.d/50-udev-default.rules.in b/rules.d/50-udev-default.rules.in
+index cef78f9..50747a1 100644
+--- a/rules.d/50-udev-default.rules.in
++++ b/rules.d/50-udev-default.rules.in
+@@ -10,9 +10,8 @@ SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-
+ SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc"
+ SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100"
+
+-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb", GOTO="default_hwdb_imported"
++SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+ ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}"
+-LABEL="default_hwdb_imported"
+
+ ACTION!="add", GOTO="default_end"
+
+diff --git a/rules.d/60-serial.rules b/rules.d/60-serial.rules
+index b162665..f303e27 100644
+--- a/rules.d/60-serial.rules
++++ b/rules.d/60-serial.rules
+@@ -4,9 +4,8 @@ ACTION=="remove", GOTO="serial_end"
+ SUBSYSTEM!="tty", GOTO="serial_end"
+
+ SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+-# We already ran the hwdb builtin for devices with MODALIAS in 50-default.rules.
+-# Let's cover the remaining case here, where we walk up the tree to find a node with $MODALIAS.
+-ENV{MODALIAS}=="", SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
++SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
++SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+
+ # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices
+ KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end"
diff --git a/debian/patches/analyze-slightly-reword-PrivateTmp-message.patch b/debian/patches/analyze-slightly-reword-PrivateTmp-message.patch
new file mode 100644
index 0000000..e6d9f1a
--- /dev/null
+++ b/debian/patches/analyze-slightly-reword-PrivateTmp-message.patch
@@ -0,0 +1,26 @@
+From: Lennart Poettering <lennart@poettering.net>
+Date: Wed, 10 Feb 2021 10:50:23 +0100
+Subject: analyze: slightly reword PrivateTmp= message
+
+Apparently there way confusion about "does not apply". Let's say "is not
+appropriate".
+
+Fixes: #13095
+(cherry picked from commit 77552b9520ba0d47cbf33cdbe1ddedb9ce9b5bf3)
+---
+ src/analyze/analyze-security.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c
+index 8d94fbc..99ec7b5 100644
+--- a/src/analyze/analyze-security.c
++++ b/src/analyze/analyze-security.c
+@@ -1545,7 +1545,7 @@ static int assess(const struct security_info *info, Table *overview_table, Analy
+
+ if (a->default_dependencies_only && !info->default_dependencies) {
+ badness = UINT64_MAX;
+- d = strdup("Service runs in special boot phase, option does not apply");
++ d = strdup("Service runs in special boot phase, option is not appropriate");
+ if (!d)
+ return log_oom();
+ } else {
diff --git a/debian/patches/ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch b/debian/patches/ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch
new file mode 100644
index 0000000..de1b1f2
--- /dev/null
+++ b/debian/patches/ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch
@@ -0,0 +1,37 @@
+From: Aleksey Vasenev <margtu-fivt@ya.ru>
+Date: Wed, 5 Oct 2022 22:33:53 +0300
+Subject: ata_id: Fixed getting Response Code from SCSI Sense Data (#24921)
+
+The Response Code is contained in the first byte of the SCSI Sense Data.
+Bit number 7 is reserved or has a different meaning for some Response Codes
+and is set to 1 for some drives.
+
+(cherry picked from commit 2be1ae54badf7a3a12908a8094ebaba8f91887ca)
+---
+ src/udev/ata_id/ata_id.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c
+index ce0bf5d..c86e40b 100644
+--- a/src/udev/ata_id/ata_id.c
++++ b/src/udev/ata_id/ata_id.c
+@@ -162,8 +162,8 @@ static int disk_identify_command(
+ return ret;
+ }
+
+- if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c) &&
+- !(sense[0] == 0x70 && sense[12] == 0x00 && sense[13] == 0x1d)) {
++ if (!((sense[0] & 0x7f) == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c) &&
++ !((sense[0] & 0x7f) == 0x70 && sense[12] == 0x00 && sense[13] == 0x1d)) {
+ errno = EIO;
+ return -1;
+ }
+@@ -240,7 +240,7 @@ static int disk_identify_packet_device_command(
+ return ret;
+ }
+
+- if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) {
++ if (!((sense[0] & 0x7f) == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) {
+ errno = EIO;
+ return -1;
+ }
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 0000000..49207c3
--- /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
new file mode 100644
index 0000000..d83b1d7
--- /dev/null
+++ b/debian/patches/basic-unit-name-adjust-comments.patch
@@ -0,0 +1,36 @@
+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-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 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) {
+ if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */
+ return -ENAMETOOLONG;
+
+- /* Refuse this if this got too long or for some other reason didn't result in a valid name */
++ /* Refuse if this for some other reason didn't result in a valid name */
+ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
+ return -EINVAL;
+
+@@ -562,7 +562,7 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha
+ if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */
+ return -ENAMETOOLONG;
+
+- /* Refuse this if this got too long or for some other reason didn't result in a valid name */
++ /* Refuse if this 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/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
new file mode 100644
index 0000000..b080d25
--- /dev/null
+++ b/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch
@@ -0,0 +1,65 @@
+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
+
+The path may have unbounded length, for example through a fuse mount.
+
+CVE-2021-33910: attacked controlled alloca() leads to crash in systemd and
+ultimately a kernel panic. Systemd parses the content of /proc/self/mountinfo
+and each mountpoint is passed to mount_setup_unit(), which calls
+unit_name_path_escape() underneath. A local attacker who is able to mount a
+filesystem with a very long path can crash systemd and the whole system.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1970887
+
+The resulting string length is bounded by UNIT_NAME_MAX, which is 256. But we
+can't easily check the length after simplification before doing the
+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 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) {
+ }
+
+ int unit_name_path_escape(const char *f, char **ret) {
+- char *p, *s;
++ _cleanup_free_ char *p = NULL;
++ char *s;
+
+ assert(f);
+ assert(ret);
+
+- p = strdupa(f);
++ p = strdup(f);
+ if (!p)
+ return -ENOMEM;
+
+@@ -395,13 +396,9 @@ int unit_name_path_escape(const char *f, char **ret) {
+ if (!path_is_normalized(p))
+ return -EINVAL;
+
+- /* Truncate trailing slashes */
++ /* Truncate trailing slashes and skip leading slashes */
+ delete_trailing_chars(p, "/");
+-
+- /* Truncate leading slashes */
+- p = skip_leading_chars(p, "/");
+-
+- s = unit_name_escape(p);
++ s = unit_name_escape(skip_leading_chars(p, "/"));
+ }
+ if (!s)
+ return -ENOMEM;
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@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/core-fix-mtime-calculation-of-dropin-files.patch b/debian/patches/core-fix-mtime-calculation-of-dropin-files.patch
new file mode 100644
index 0000000..4c6b1e8
--- /dev/null
+++ b/debian/patches/core-fix-mtime-calculation-of-dropin-files.patch
@@ -0,0 +1,100 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Thu, 4 Mar 2021 00:36:24 +0100
+Subject: core: fix mtime calculation of dropin files
+
+Nominally, the bug was in unit_load_dropin(), which just took the last mtime
+instead of calculating the maximum. But instead of adding code to wrap the
+loop, this patch goes in the other direction.
+
+All (correct) callers of config_parse() followed a very similar pattern to
+calculate the maximum mtime. So let's simplify things by making config_parse()
+assume that mtime is initialized and update it to the maximum. This makes all
+the callers that care about mtime simpler and also fixes the issue in
+unit_load_dropin().
+
+config_parse_many_nulstr() and config_parse_many() are different, because it
+makes sense to call them just once, and current ret_mtime behaviour make sense.
+
+Fixes #17730, https://bugzilla.redhat.com/show_bug.cgi?id=1933137.
+
+(cherry picked from commit da46a1bc3cd28ac36114002c216196dae004b05c)
+---
+ src/core/load-dropin.c | 1 +
+ src/shared/conf-parser.c | 15 +++++++--------
+ src/shared/conf-parser.h | 2 +-
+ 3 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
+index d1c85e2..3bb4856 100644
+--- a/src/core/load-dropin.c
++++ b/src/core/load-dropin.c
+@@ -112,6 +112,7 @@ int unit_load_dropin(Unit *u) {
+ return log_oom();
+ }
+
++ u->dropin_mtime = 0;
+ STRV_FOREACH(f, u->dropin_paths)
+ (void) config_parse(
+ u->id, *f, NULL,
+diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
+index 35d301d..099c47a 100644
+--- a/src/shared/conf-parser.c
++++ b/src/shared/conf-parser.c
+@@ -259,7 +259,7 @@ int config_parse(const char *unit,
+ const void *table,
+ ConfigParseFlags flags,
+ void *userdata,
+- usec_t *ret_mtime) {
++ usec_t *latest_mtime) {
+
+ _cleanup_free_ char *section = NULL, *continuation = NULL;
+ _cleanup_fclose_ FILE *ours = NULL;
+@@ -271,6 +271,9 @@ int config_parse(const char *unit,
+ assert(filename);
+ assert(lookup);
+
++ /* latest_mtime is an input-output parameter: it will be updated if the mtime of the file we're
++ * looking at is later than the current *latest_mtime value. */
++
+ if (!f) {
+ f = ours = fopen(filename, "re");
+ if (!f) {
+@@ -413,8 +416,8 @@ int config_parse(const char *unit,
+ }
+ }
+
+- if (ret_mtime)
+- *ret_mtime = mtime;
++ if (latest_mtime)
++ *latest_mtime = MAX(*latest_mtime, mtime);
+
+ return 0;
+ }
+@@ -440,13 +443,9 @@ static int config_parse_many_files(
+ }
+
+ STRV_FOREACH(fn, files) {
+- usec_t t;
+-
+- r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &t);
++ r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &mtime);
+ if (r < 0)
+ return r;
+- if (t > mtime) /* Find the newest */
+- mtime = t;
+ }
+
+ if (ret_mtime)
+diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
+index f115cb2..84c9bf6 100644
+--- a/src/shared/conf-parser.h
++++ b/src/shared/conf-parser.h
+@@ -89,7 +89,7 @@ int config_parse(
+ const void *table,
+ ConfigParseFlags flags,
+ void *userdata,
+- usec_t *ret_mtime); /* possibly NULL */
++ usec_t *latest_mtime); /* input/output, possibly NULL */
+
+ int config_parse_many_nulstr(
+ const char *conf_file, /* possibly NULL */
diff --git a/debian/patches/coredump-do-not-allow-user-to-access-coredumps-with-chang.patch b/debian/patches/coredump-do-not-allow-user-to-access-coredumps-with-chang.patch
new file mode 100644
index 0000000..f1029d3
--- /dev/null
+++ b/debian/patches/coredump-do-not-allow-user-to-access-coredumps-with-chang.patch
@@ -0,0 +1,388 @@
+From: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+Date: Mon, 28 Nov 2022 12:12:55 +0100
+Subject: coredump: do not allow user to access coredumps with changed
+ uid/gid/capabilities
+
+When the user starts a program which elevates its permissions via setuid,
+setgid, or capabilities set on the file, it may access additional information
+which would then be visible in the coredump. We shouldn't make the the coredump
+visible to the user in such cases.
+
+Reported-by: Matthias Gerstner <mgerstner@suse.de>
+
+This reads the /proc/<pid>/auxv file and attaches it to the process metadata as
+PROC_AUXV. Before the coredump is submitted, it is parsed and if either
+at_secure was set (which the kernel will do for processes that are setuid,
+setgid, or setcap), or if the effective uid/gid don't match uid/gid, the file
+is not made accessible to the user. If we can't access this data, we assume the
+file should not be made accessible either. In principle we could also access
+the auxv data from a note in the core file, but that is much more complex and
+it seems better to use the stand-alone file that is provided by the kernel.
+
+Attaching auxv is both convient for this patch (because this way it's passed
+between the stages along with other fields), but I think it makes sense to save
+it in general.
+
+We use the information early in the core file to figure out if the program was
+32-bit or 64-bit and its endianness. This way we don't need heuristics to guess
+whether the format of the auxv structure. This test might reject some cases on
+fringe architecutes. But the impact would be limited: we just won't grant the
+user permissions to view the coredump file. If people report that we're missing
+some cases, we can always enhance this to support more architectures.
+
+I tested auxv parsing on amd64, 32-bit program on amd64, arm64, arm32, and
+ppc64el, but not the whole coredump handling.
+
+(cherry picked from commit 3e4d0f6cf99f8677edd6a237382a65bfe758de03)
+(cherry picked from commit 9b75a3d0502d6741c8ecb7175794345f8eb3827c)
+(cherry picked from commit efca5283dc791a07171f80eef84e14fdb58fad57)
+(cherry picked from commit 1d5e0e9910500f3c3584485f77bfc35e601036e3)
+(cherry picked from commit 8215e1527d859e77dd1378fd7e42bbd32130edb3)
+(cherry picked from commit 786df410b1cb3a2294c9a5d118c958525e7439e6)
+---
+ src/basic/io-util.h | 9 +++
+ src/coredump/coredump.c | 200 ++++++++++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 194 insertions(+), 15 deletions(-)
+
+diff --git a/src/basic/io-util.h b/src/basic/io-util.h
+index d817714..dacec71 100644
+--- a/src/basic/io-util.h
++++ b/src/basic/io-util.h
+@@ -85,7 +85,16 @@ struct iovec_wrapper *iovw_new(void);
+ struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
+ struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
+ void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
++
+ int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
++static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
++ /* Move data into iovw or free on error */
++ int r = iovw_put(iovw, data, len);
++ if (r < 0)
++ free(data);
++ return r;
++}
++
+ int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
+ int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
+ void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
+diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
+index 0a1cb91..b60dff3 100644
+--- a/src/coredump/coredump.c
++++ b/src/coredump/coredump.c
+@@ -3,6 +3,7 @@
+ #include <errno.h>
+ #include <stdio.h>
+ #include <sys/prctl.h>
++#include <sys/auxv.h>
+ #include <sys/xattr.h>
+ #include <unistd.h>
+
+@@ -96,6 +97,7 @@ enum {
+
+ META_EXE = _META_MANDATORY_MAX,
+ META_UNIT,
++ META_PROC_AUXV,
+ _META_MAX
+ };
+
+@@ -110,10 +112,12 @@ static const char * const meta_field_names[_META_MAX] = {
+ [META_COMM] = "COREDUMP_COMM=",
+ [META_EXE] = "COREDUMP_EXE=",
+ [META_UNIT] = "COREDUMP_UNIT=",
++ [META_PROC_AUXV] = "COREDUMP_PROC_AUXV=",
+ };
+
+ typedef struct Context {
+ const char *meta[_META_MAX];
++ size_t meta_size[_META_MAX];
+ pid_t pid;
+ bool is_pid1;
+ bool is_journald;
+@@ -175,14 +179,17 @@ static uint64_t storage_size_max(void) {
+ return 0;
+ }
+
+-static int fix_acl(int fd, uid_t uid) {
+-
+-#if HAVE_ACL
+- int r;
+-
++static int fix_acl(int fd, uid_t uid, bool allow_user) {
+ assert(fd >= 0);
+ assert(uid_is_valid(uid));
+
++#if HAVE_ACL
++ int r;
++
++ /* We don't allow users to read coredumps if the uid or capabilities were changed. */
++ if (!allow_user)
++ return 0;
++
+ if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
+ return 0;
+
+@@ -242,7 +249,8 @@ static int fix_permissions(
+ const char *filename,
+ const char *target,
+ const Context *context,
+- uid_t uid) {
++ uid_t uid,
++ bool allow_user) {
+
+ int r;
+
+@@ -252,7 +260,7 @@ static int fix_permissions(
+
+ /* Ignore errors on these */
+ (void) fchmod(fd, 0640);
+- (void) fix_acl(fd, uid);
++ (void) fix_acl(fd, uid, allow_user);
+ (void) fix_xattr(fd, context);
+
+ if (fsync(fd) < 0)
+@@ -323,6 +331,153 @@ static int make_filename(const Context *context, char **ret) {
+ return 0;
+ }
+
++static int parse_auxv64(
++ const uint64_t *auxv,
++ size_t size_bytes,
++ int *at_secure,
++ uid_t *uid,
++ uid_t *euid,
++ gid_t *gid,
++ gid_t *egid) {
++
++ assert(auxv || size_bytes == 0);
++
++ if (size_bytes % (2 * sizeof(uint64_t)) != 0)
++ return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes);
++
++ size_t words = size_bytes / sizeof(uint64_t);
++
++ /* Note that we set output variables even on error. */
++
++ for (size_t i = 0; i + 1 < words; i += 2)
++ switch (auxv[i]) {
++ case AT_SECURE:
++ *at_secure = auxv[i + 1] != 0;
++ break;
++ case AT_UID:
++ *uid = auxv[i + 1];
++ break;
++ case AT_EUID:
++ *euid = auxv[i + 1];
++ break;
++ case AT_GID:
++ *gid = auxv[i + 1];
++ break;
++ case AT_EGID:
++ *egid = auxv[i + 1];
++ break;
++ case AT_NULL:
++ if (auxv[i + 1] != 0)
++ goto error;
++ return 0;
++ }
++ error:
++ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA),
++ "AT_NULL terminator not found, cannot parse auxv structure.");
++}
++
++static int parse_auxv32(
++ const uint32_t *auxv,
++ size_t size_bytes,
++ int *at_secure,
++ uid_t *uid,
++ uid_t *euid,
++ gid_t *gid,
++ gid_t *egid) {
++
++ assert(auxv || size_bytes == 0);
++
++ size_t words = size_bytes / sizeof(uint32_t);
++
++ if (size_bytes % (2 * sizeof(uint32_t)) != 0)
++ return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes);
++
++ /* Note that we set output variables even on error. */
++
++ for (size_t i = 0; i + 1 < words; i += 2)
++ switch (auxv[i]) {
++ case AT_SECURE:
++ *at_secure = auxv[i + 1] != 0;
++ break;
++ case AT_UID:
++ *uid = auxv[i + 1];
++ break;
++ case AT_EUID:
++ *euid = auxv[i + 1];
++ break;
++ case AT_GID:
++ *gid = auxv[i + 1];
++ break;
++ case AT_EGID:
++ *egid = auxv[i + 1];
++ break;
++ case AT_NULL:
++ if (auxv[i + 1] != 0)
++ goto error;
++ return 0;
++ }
++ error:
++ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA),
++ "AT_NULL terminator not found, cannot parse auxv structure.");
++}
++
++static int grant_user_access(int core_fd, const Context *context) {
++ int at_secure = -1;
++ uid_t uid = UID_INVALID, euid = UID_INVALID;
++ uid_t gid = GID_INVALID, egid = GID_INVALID;
++ int r;
++
++ assert(core_fd >= 0);
++ assert(context);
++
++ if (!context->meta[META_PROC_AUXV])
++ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), "No auxv data, not adjusting permissions.");
++
++ uint8_t elf[EI_NIDENT];
++ errno = 0;
++ if (pread(core_fd, &elf, sizeof(elf), 0) != sizeof(elf))
++ return log_warning_errno(errno_or_else(EIO),
++ "Failed to pread from coredump fd: %s", errno != 0 ? strerror_safe(errno) : "Unexpected EOF");
++
++ if (elf[EI_MAG0] != ELFMAG0 ||
++ elf[EI_MAG1] != ELFMAG1 ||
++ elf[EI_MAG2] != ELFMAG2 ||
++ elf[EI_MAG3] != ELFMAG3 ||
++ elf[EI_VERSION] != EV_CURRENT)
++ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN),
++ "Core file does not have ELF header, not adjusting permissions.");
++ if (!IN_SET(elf[EI_CLASS], ELFCLASS32, ELFCLASS64) ||
++ !IN_SET(elf[EI_DATA], ELFDATA2LSB, ELFDATA2MSB))
++ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN),
++ "Core file has strange ELF class, not adjusting permissions.");
++
++ if ((elf[EI_DATA] == ELFDATA2LSB) != (__BYTE_ORDER == __LITTLE_ENDIAN))
++ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN),
++ "Core file has non-native endianness, not adjusting permissions.");
++
++ if (elf[EI_CLASS] == ELFCLASS64)
++ r = parse_auxv64((const uint64_t*) context->meta[META_PROC_AUXV],
++ context->meta_size[META_PROC_AUXV],
++ &at_secure, &uid, &euid, &gid, &egid);
++ else
++ r = parse_auxv32((const uint32_t*) context->meta[META_PROC_AUXV],
++ context->meta_size[META_PROC_AUXV],
++ &at_secure, &uid, &euid, &gid, &egid);
++ if (r < 0)
++ return r;
++
++ /* We allow access if we got all the data and at_secure is not set and
++ * the uid/gid matches euid/egid. */
++ bool ret =
++ at_secure == 0 &&
++ uid != UID_INVALID && euid != UID_INVALID && uid == euid &&
++ gid != GID_INVALID && egid != GID_INVALID && gid == egid;
++ log_debug("Will %s access (uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)",
++ ret ? "permit" : "restrict",
++ uid, euid, gid, egid, yes_no(at_secure));
++ return ret;
++}
++
+ static int save_external_coredump(
+ const Context *context,
+ int input_fd,
+@@ -403,6 +558,8 @@ static int save_external_coredump(
+ goto fail;
+ }
+
++ bool allow_user = grant_user_access(fd, context) > 0;
++
+ #if HAVE_COMPRESSION
+ /* If we will remove the coredump anyway, do not compress. */
+ if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) {
+@@ -428,7 +585,7 @@ static int save_external_coredump(
+ goto fail_compressed;
+ }
+
+- r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
++ r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid, allow_user);
+ if (r < 0)
+ goto fail_compressed;
+
+@@ -451,7 +608,7 @@ static int save_external_coredump(
+ uncompressed:
+ #endif
+
+- r = fix_permissions(fd, tmp, fn, context, uid);
++ r = fix_permissions(fd, tmp, fn, context, uid, allow_user);
+ if (r < 0)
+ goto fail;
+
+@@ -700,7 +857,7 @@ static int change_uid_gid(const Context *context) {
+ }
+
+ static int submit_coredump(
+- Context *context,
++ const Context *context,
+ struct iovec_wrapper *iovw,
+ int input_fd) {
+
+@@ -822,16 +979,15 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) {
+ struct iovec *iovec = iovw->iovec + n;
+
+ for (i = 0; i < ELEMENTSOF(meta_field_names); i++) {
+- char *p;
+-
+ /* Note that these strings are NUL terminated, because we made sure that a
+ * trailing NUL byte is in the buffer, though not included in the iov_len
+ * count (see process_socket() and gather_pid_metadata_*()) */
+ assert(((char*) iovec->iov_base)[iovec->iov_len] == 0);
+
+- p = startswith(iovec->iov_base, meta_field_names[i]);
++ const char *p = startswith(iovec->iov_base, meta_field_names[i]);
+ if (p) {
+ context->meta[i] = p;
++ context->meta_size[i] = iovec->iov_len - strlen(meta_field_names[i]);
+ count++;
+ break;
+ }
+@@ -1074,6 +1230,7 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
+ uid_t owner_uid;
+ pid_t pid;
+ char *t;
++ size_t size;
+ const char *p;
+ int r;
+
+@@ -1139,13 +1296,26 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_LIMITS=", t);
+
+ p = procfs_file_alloca(pid, "cgroup");
+- if (read_full_file(p, &t, NULL) >=0)
++ if (read_full_file(p, &t, NULL) >= 0)
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_CGROUP=", t);
+
+ p = procfs_file_alloca(pid, "mountinfo");
+- if (read_full_file(p, &t, NULL) >=0)
++ if (read_full_file(p, &t, NULL) >= 0)
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_MOUNTINFO=", t);
+
++ /* We attach /proc/auxv here. ELF coredumps also contain a note for this (NT_AUXV), see elf(5). */
++ p = procfs_file_alloca(pid, "auxv");
++ if (read_full_virtual_file(p, &t, &size) >= 0) {
++ char *buf = malloc(strlen("COREDUMP_PROC_AUXV=") + size + 1);
++ if (buf) {
++ /* Add a dummy terminator to make save_context() happy. */
++ *((uint8_t*) mempcpy(stpcpy(buf, "COREDUMP_PROC_AUXV="), t, size)) = '\0';
++ (void) iovw_consume(iovw, buf, size + strlen("COREDUMP_PROC_AUXV="));
++ }
++
++ free(t);
++ }
++
+ if (get_process_cwd(pid, &t) >= 0)
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_CWD=", t);
+
diff --git a/debian/patches/debian/Add-env-variable-for-machine-ID-path.patch b/debian/patches/debian/Add-env-variable-for-machine-ID-path.patch
new file mode 100644
index 0000000..7645d97
--- /dev/null
+++ b/debian/patches/debian/Add-env-variable-for-machine-ID-path.patch
@@ -0,0 +1,77 @@
+From: Martin Pitt <mpitt@debian.org>
+Date: Wed, 18 Jan 2017 11:21:35 +0100
+Subject: Add env variable for machine ID path
+
+During package build, in minimal chroots, or other systems which do not already
+have an /etc/machine-id we get six test failures. Introduce a
+$SYSTEMD_MACHINE_ID_PATH environment variable which can specify a location
+other than /etc/machine-id, so that the unit tests are independent from the
+environment.
+
+Also adjust test-fs-util to not assume that /etc/machine-id exists. Use
+/etc/passwd instead which is created by base-files.
+
+Closes: #851445
+
+Bug: https://bugs.freedesktop.org/show_bug.cgi?id=62344
+---
+ src/libsystemd/sd-id128/sd-id128.c | 2 +-
+ src/test/test-fs-util.c | 11 +++++++----
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
+index d5de935..78612a0 100644
+--- a/src/libsystemd/sd-id128/sd-id128.c
++++ b/src/libsystemd/sd-id128/sd-id128.c
+@@ -88,7 +88,7 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
+ assert_return(ret, -EINVAL);
+
+ if (sd_id128_is_null(saved_machine_id)) {
+- r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
++ r = id128_read(getenv("SYSTEMD_MACHINE_ID_PATH") ?: "/etc/machine-id", ID128_PLAIN, &saved_machine_id);
+ if (r < 0)
+ return r;
+
+diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
+index d1f9252..4c93adb 100644
+--- a/src/test/test-fs-util.c
++++ b/src/test/test-fs-util.c
+@@ -211,7 +211,7 @@ static void test_chase_symlinks(void) {
+ assert_se(streq(result, "/test-chase.fsldajfl"));
+ result = mfree(result);
+
+- r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result, NULL);
++ r = chase_symlinks("/etc/passwd/foo", NULL, 0, &result, NULL);
+ assert_se(r == -ENOTDIR);
+ result = mfree(result);
+
+@@ -284,23 +284,26 @@ static void test_chase_symlinks(void) {
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+ }
+
+- p = strjoina(temp, "/machine-id-test");
+- assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
++ p = strjoina(temp, "/passwd-test");
++ assert_se(symlink("/usr/../etc/./passwd", p) >= 0);
+
+ r = chase_symlinks(p, NULL, 0, NULL, &pfd);
+ if (r != -ENOENT) {
+ _cleanup_close_ int fd = -1;
++/*
+ sd_id128_t a, b;
++*/
+
+ assert_se(pfd >= 0);
+
+ fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
+ assert_se(fd >= 0);
+ safe_close(pfd);
+-
++/*
+ assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
+ assert_se(sd_id128_get_machine(&b) >= 0);
+ assert_se(sd_id128_equal(a, b));
++*/
+ }
+
+ /* Test CHASE_NOFOLLOW */
diff --git a/debian/patches/debian/Add-support-for-TuxOnIce-hibernation.patch b/debian/patches/debian/Add-support-for-TuxOnIce-hibernation.patch
new file mode 100644
index 0000000..226dea2
--- /dev/null
+++ b/debian/patches/debian/Add-support-for-TuxOnIce-hibernation.patch
@@ -0,0 +1,30 @@
+From: Julien Muchembled <jm@jmuchemb.eu>
+Date: Tue, 29 Apr 2014 11:40:50 +0200
+Subject: Add support for TuxOnIce hibernation
+
+systemd does not support non-mainline kernel features so upstream rejected this
+patch.
+It is however required for systemd integration by tuxonice-userui package.
+
+Forwarded: http://lists.freedesktop.org/archives/systemd-devel/2014-April/018960.html
+---
+ src/shared/sleep-config.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
+index cea5148..66e1675 100644
+--- a/src/shared/sleep-config.c
++++ b/src/shared/sleep-config.c
+@@ -471,6 +471,12 @@ static bool enough_swap_for_hibernation(void) {
+ if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
+ return true;
+
++ /* TuxOnIce is an alternate implementation for hibernation.
++ * It can be configured to compress the image to a file or an inactive
++ * swap partition, so there's nothing more we can do here. */
++ if (access("/sys/power/tuxonice", F_OK) == 0)
++ return true;
++
+ r = find_hibernate_location(&hibernate_location);
+ if (r < 0)
+ return false;
diff --git a/debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch b/debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch
new file mode 100644
index 0000000..d99ea42
--- /dev/null
+++ b/debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch
@@ -0,0 +1,21 @@
+From: Tollef Fog Heen <tfheen@err.no>
+Date: Tue, 5 Jun 2012 20:59:36 +0200
+Subject: Bring tmpfiles.d/tmp.conf in line with Debian defaults
+
+Closes: #675422
+---
+ tmpfiles.d/tmp.conf | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/tmpfiles.d/tmp.conf b/tmpfiles.d/tmp.conf
+index fe5225d..39cb5cc 100644
+--- a/tmpfiles.d/tmp.conf
++++ b/tmpfiles.d/tmp.conf
+@@ -8,5 +8,5 @@
+ # See tmpfiles.d(5) for details
+
+ # Clear tmp directories separately, to make them easier to override
+-q /tmp 1777 root root 10d
+-q /var/tmp 1777 root root 30d
++D /tmp 1777 root root -
++#q /var/tmp 1777 root root 30d
diff --git a/debian/patches/debian/Don-t-enable-audit-by-default.patch b/debian/patches/debian/Don-t-enable-audit-by-default.patch
new file mode 100644
index 0000000..b4cf902
--- /dev/null
+++ b/debian/patches/debian/Don-t-enable-audit-by-default.patch
@@ -0,0 +1,53 @@
+From: Martin Pitt <martin.pitt@ubuntu.com>
+Date: Sun, 28 Dec 2014 12:49:35 +0100
+Subject: Don't enable audit by default
+
+It causes flooding of dmesg and syslog, suppressing actually important
+messages.
+
+Don't enable it for now, until a better solution is found:
+http://lists.freedesktop.org/archives/systemd-devel/2014-December/026591.html
+
+Bug-Debian: https://bugs.debian.org/773528
+---
+ man/journald.conf.xml | 2 +-
+ src/journal/journald-server.c | 2 +-
+ src/journal/journald.conf | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/man/journald.conf.xml b/man/journald.conf.xml
+index 2134a1d..a4a42a4 100644
+--- a/man/journald.conf.xml
++++ b/man/journald.conf.xml
+@@ -417,7 +417,7 @@
+ <command>systemd-journald</command> collects generated audit records, it just controls whether it
+ tells the kernel to generate them. This means if another tool turns on auditing even if
+ <command>systemd-journald</command> left it off, it will still collect the generated
+- messages. Defaults to on.</para></listitem>
++ messages. Defaults to off.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
+index ce15766..bd7fa8f 100644
+--- a/src/journal/journald-server.c
++++ b/src/journal/journald-server.c
+@@ -2188,7 +2188,7 @@ int server_init(Server *s, const char *namespace) {
+ .compress.threshold_bytes = (uint64_t) -1,
+ .seal = true,
+
+- .set_audit = true,
++ .set_audit = false,
+
+ .watchdog_usec = USEC_INFINITY,
+
+diff --git a/src/journal/journald.conf b/src/journal/journald.conf
+index d6cd5b1..5e4b0e2 100644
+--- a/src/journal/journald.conf
++++ b/src/journal/journald.conf
+@@ -41,4 +41,4 @@
+ #MaxLevelWall=emerg
+ #LineMax=48K
+ #ReadKMsg=yes
+-#Audit=yes
++#Audit=no
diff --git a/debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch b/debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch
new file mode 100644
index 0000000..1b5b03d
--- /dev/null
+++ b/debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch
@@ -0,0 +1,74 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Tue, 16 Feb 2021 00:18:50 +0100
+Subject: Downgrade a couple of warnings to debug
+
+If a package still ships only a SysV init script or if a service file or
+tmpfile uses /var/run, downgrade those messages to debug. We can use
+lintian to detect those issues.
+For service files and tmpfiles in /etc, keep the warning, as those files
+are typically added locally and aren't checked by lintian.
+
+Closes: #981407
+---
+ src/core/load-fragment.c | 4 +++-
+ src/sysv-generator/sysv-generator.c | 2 +-
+ src/tmpfiles/tmpfiles.c | 4 +++-
+ 3 files changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
+index 5b66fb1..df5669a 100644
+--- a/src/core/load-fragment.c
++++ b/src/core/load-fragment.c
+@@ -372,6 +372,7 @@ static int patch_var_run(
+
+ const char *e;
+ char *z;
++ int log_level;
+
+ e = path_startswith(*path, "/var/run/");
+ if (!e)
+@@ -381,7 +382,8 @@ static int patch_var_run(
+ if (!z)
+ return log_oom();
+
+- log_syntax(unit, LOG_NOTICE, filename, line, 0,
++ log_level = path_startswith(filename, "/etc") ? LOG_NOTICE : LOG_DEBUG;
++ log_syntax(unit, log_level, filename, line, 0,
+ "%s= references a path below legacy directory /var/run/, updating %s → %s; "
+ "please update the unit file accordingly.", lvalue, *path, z);
+
+diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
+index 008a825..ab0054e 100644
+--- a/src/sysv-generator/sysv-generator.c
++++ b/src/sysv-generator/sysv-generator.c
+@@ -787,7 +787,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
+ if (!fpath)
+ return log_oom();
+
+- log_warning("SysV service '%s' lacks a native systemd unit file. "
++ log_debug("SysV service '%s' lacks a native systemd unit file. "
+ "Automatically generating a unit file for compatibility. "
+ "Please update package to include a native systemd unit file, in order to make it more safe and robust.", fpath);
+
+diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
+index 9906c70..31e5707 100644
+--- a/src/tmpfiles/tmpfiles.c
++++ b/src/tmpfiles/tmpfiles.c
+@@ -2538,6 +2538,7 @@ static int specifier_expansion_from_arg(Item *i) {
+ static int patch_var_run(const char *fname, unsigned line, char **path) {
+ const char *k;
+ char *n;
++ int log_level;
+
+ assert(path);
+ assert(*path);
+@@ -2563,7 +2564,8 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
+ /* Also log about this briefly. We do so at LOG_NOTICE level, as we fixed up the situation automatically, hence
+ * there's no immediate need for action by the user. However, in the interest of making things less confusing
+ * to the user, let's still inform the user that these snippets should really be updated. */
+- log_syntax(NULL, LOG_NOTICE, fname, line, 0, "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", *path, n);
++ log_level = path_startswith(fname, "/etc") ? LOG_NOTICE : LOG_DEBUG;
++ log_syntax(NULL, log_level, fname, line, 0, "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", *path, n);
+
+ free_and_replace(*path, n);
+
diff --git a/debian/patches/debian/Drop-seccomp-system-call-filter-for-udev.patch b/debian/patches/debian/Drop-seccomp-system-call-filter-for-udev.patch
new file mode 100644
index 0000000..4ce4884
--- /dev/null
+++ b/debian/patches/debian/Drop-seccomp-system-call-filter-for-udev.patch
@@ -0,0 +1,31 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Wed, 18 Jul 2018 23:49:16 +0200
+Subject: Drop seccomp system call filter for udev
+
+The seccomp based system call whitelist requires at least systemd 239 to
+be the active init and during a dist-upgrade we can't guarantee that
+systemd has been fully configured before udev is restarted.
+
+This partially reverts upstream commit
+ee8f26180d01e3ddd4e5f20b03b81e5e737657ae.
+
+Once buster is released, this patch can be dropped.
+
+Closes: #903224
+---
+ units/systemd-udevd.service.in | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in
+index f3458d9..225eac2 100644
+--- a/units/systemd-udevd.service.in
++++ b/units/systemd-udevd.service.in
+@@ -35,8 +35,6 @@ MemoryDenyWriteExecute=yes
+ RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
+ RestrictRealtime=yes
+ RestrictSUIDSGID=yes
+-SystemCallFilter=@system-service @module @raw-io
+-SystemCallErrorNumber=EPERM
+ SystemCallArchitectures=native
+ LockPersonality=yes
+ IPAddressDeny=any
diff --git a/debian/patches/debian/Keep-journal-files-compatible-with-older-versions.patch b/debian/patches/debian/Keep-journal-files-compatible-with-older-versions.patch
new file mode 100644
index 0000000..3e9aa5a
--- /dev/null
+++ b/debian/patches/debian/Keep-journal-files-compatible-with-older-versions.patch
@@ -0,0 +1,69 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Mon, 17 Aug 2020 22:11:19 +0200
+Subject: Keep journal files compatible with older versions
+
+Disable the KEYED-HASH journal feature by default and keep LZ4 (instead
+of ZSTD) as default compression for new journal files. Otherwise journal
+files are incompatible and can't be read by older journalctl
+implementations.
+
+This patch can be dropped in bullseye+1, as journalctl from bullseye
+will then be able to read journal files with those features.
+
+Closes: #968055
+---
+ src/journal/compress.h | 10 +++++-----
+ src/journal/journal-file.c | 8 ++++----
+ 2 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/src/journal/compress.h b/src/journal/compress.h
+index db7f399..6cd9290 100644
+--- a/src/journal/compress.h
++++ b/src/journal/compress.h
+@@ -18,14 +18,14 @@ int compress_blob_zstd(const void *src, uint64_t src_size,
+ static inline int compress_blob(const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size) {
+ int r;
+-#if HAVE_ZSTD
+- r = compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size);
+- if (r == 0)
+- return OBJECT_COMPRESSED_ZSTD;
+-#elif HAVE_LZ4
++#if HAVE_LZ4
+ r = compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size);
+ if (r == 0)
+ return OBJECT_COMPRESSED_LZ4;
++#elif HAVE_ZSTD
++ r = compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size);
++ if (r == 0)
++ return OBJECT_COMPRESSED_ZSTD;
+ #elif HAVE_XZ
+ r = compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size);
+ if (r == 0)
+diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
+index 15336be..6ce18c9 100644
+--- a/src/journal/journal-file.c
++++ b/src/journal/journal-file.c
+@@ -3411,10 +3411,10 @@ int journal_file_open(
+ .prot = prot_from_flags(flags),
+ .writable = (flags & O_ACCMODE) != O_RDONLY,
+
+-#if HAVE_ZSTD
+- .compress_zstd = compress,
+-#elif HAVE_LZ4
++#if HAVE_LZ4
+ .compress_lz4 = compress,
++#elif HAVE_ZSTD
++ .compress_zstd = compress,
+ #elif HAVE_XZ
+ .compress_xz = compress,
+ #endif
+@@ -3432,7 +3432,7 @@ int journal_file_open(
+ if (r < 0) {
+ if (r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring.");
+- f->keyed_hash = true;
++ f->keyed_hash = false;
+ } else
+ f->keyed_hash = r;
+
diff --git a/debian/patches/debian/Let-graphical-session-pre.target-be-manually-started.patch b/debian/patches/debian/Let-graphical-session-pre.target-be-manually-started.patch
new file mode 100644
index 0000000..cf8db56
--- /dev/null
+++ b/debian/patches/debian/Let-graphical-session-pre.target-be-manually-started.patch
@@ -0,0 +1,22 @@
+From: Iain Lane <iain@orangesquash.org.uk>
+Date: Mon, 22 Aug 2016 07:03:27 +0200
+Subject: Let graphical-session-pre.target be manually started
+
+This is needed until https://github.com/systemd/systemd/issues/3750 is fixed.
+
+Forwarded: not-needed
+Bug-Ubuntu: https://launchpad.net/bugs/1615341
+---
+ units/user/graphical-session-pre.target | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/units/user/graphical-session-pre.target b/units/user/graphical-session-pre.target
+index 4b9e3dc..dffaf85 100644
+--- a/units/user/graphical-session-pre.target
++++ b/units/user/graphical-session-pre.target
+@@ -12,5 +12,4 @@ Description=Session services which should run early before the graphical session
+ Documentation=man:systemd.special(7)
+ Requires=basic.target
+ Before=graphical-session.target
+-RefuseManualStart=yes
+ StopWhenUnneeded=yes
diff --git a/debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch b/debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch
new file mode 100644
index 0000000..a596b7a
--- /dev/null
+++ b/debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch
@@ -0,0 +1,42 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Fri, 5 Sep 2014 01:15:16 +0200
+Subject: Make /run/lock tmpfs an API fs
+
+The /run/lock directory is world-writable in Debian due to historic
+reasons. To avoid user processes filling up /run, we mount a separate
+tmpfs for /run/lock. As this directory needs to be available during
+early boot, we make it an API fs.
+
+Drop it from tmpfiles.d/legacy.conf to not clobber the permissions.
+
+Closes: #751392
+---
+ src/core/mount-setup.c | 2 ++
+ tmpfiles.d/legacy.conf | 1 -
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
+index 915b101..7a330dd 100644
+--- a/src/core/mount-setup.c
++++ b/src/core/mount-setup.c
+@@ -86,6 +86,8 @@ static const MountPoint mount_table[] = {
+ #endif
+ { "tmpfs", "/run", "tmpfs", "mode=755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ NULL, MNT_FATAL|MNT_IN_CONTAINER },
++ { "tmpfs", "/run/lock", "tmpfs", "mode=1777,size=5242880", MS_NOSUID|MS_NOEXEC|MS_NODEV,
++ NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate,memory_recursiveprot", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
+ { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+diff --git a/tmpfiles.d/legacy.conf b/tmpfiles.d/legacy.conf
+index 62e2ae0..ea5e735 100644
+--- a/tmpfiles.d/legacy.conf
++++ b/tmpfiles.d/legacy.conf
+@@ -10,7 +10,6 @@
+ # These files are considered legacy and are unnecessary on legacy-free
+ # systems.
+
+-d /run/lock 0755 root root -
+ L /var/lock - - - - ../run/lock
+
+ # /run/lock/subsys is used for serializing SysV service execution, and
diff --git a/debian/patches/debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch b/debian/patches/debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch
new file mode 100644
index 0000000..ae0bb3f
--- /dev/null
+++ b/debian/patches/debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch
@@ -0,0 +1,68 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Thu, 15 Oct 2020 23:11:01 +0200
+Subject: Move sysusers.d/sysctl.d/binfmt.d/modules-load.d back to /usr
+
+In Debian, late mounting of /usr is no longer supported, so it is safe
+to install those files in /usr.
+We want those facilities in /usr, not /, as this will make an eventual
+switch to a merged-usr setup easier.
+
+Closes: #971282
+---
+ src/core/systemd.pc.in | 8 ++++----
+ src/libsystemd/sd-path/sd-path.c | 8 ++++----
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
+index b5cc8f9..21dbf30 100644
+--- a/src/core/systemd.pc.in
++++ b/src/core/systemd.pc.in
+@@ -65,16 +65,16 @@ systemdshutdowndir=${systemd_shutdown_dir}
+ tmpfiles_dir=${prefix}/lib/tmpfiles.d
+ tmpfilesdir=${tmpfiles_dir}
+
+-sysusers_dir=${rootprefix}/lib/sysusers.d
++sysusers_dir=${prefix}/lib/sysusers.d
+ sysusersdir=${sysusers_dir}
+
+-sysctl_dir=${rootprefix}/lib/sysctl.d
++sysctl_dir=${prefix}/lib/sysctl.d
+ sysctldir=${sysctl_dir}
+
+-binfmt_dir=${rootprefix}/lib/binfmt.d
++binfmt_dir=${prefix}/lib/binfmt.d
+ binfmtdir=${binfmt_dir}
+
+-modules_load_dir=${rootprefix}/lib/modules-load.d
++modules_load_dir=${prefix}/lib/modules-load.d
+ modulesloaddir=${modules_load_dir}
+
+ catalog_dir=${prefix}/lib/systemd/catalog
+diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c
+index 61ed7cb..682e3f1 100644
+--- a/src/libsystemd/sd-path/sd-path.c
++++ b/src/libsystemd/sd-path/sd-path.c
+@@ -369,19 +369,19 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
+ return 0;
+
+ case SD_PATH_SYSUSERS:
+- *ret = ROOTPREFIX_NOSLASH "/lib/sysusers.d";
++ *ret = "/usr/lib/sysusers.d";
+ return 0;
+
+ case SD_PATH_SYSCTL:
+- *ret = ROOTPREFIX_NOSLASH "/lib/sysctl.d";
++ *ret = "/usr/lib/sysctl.d";
+ return 0;
+
+ case SD_PATH_BINFMT:
+- *ret = ROOTPREFIX_NOSLASH "/lib/binfmt.d";
++ *ret = "/usr/lib/binfmt.d";
+ return 0;
+
+ case SD_PATH_MODULES_LOAD:
+- *ret = ROOTPREFIX_NOSLASH "/lib/modules-load.d";
++ *ret = "/usr/lib/modules-load.d";
+ return 0;
+
+ case SD_PATH_CATALOG:
diff --git a/debian/patches/debian/Only-start-logind-if-dbus-is-installed.patch b/debian/patches/debian/Only-start-logind-if-dbus-is-installed.patch
new file mode 100644
index 0000000..4683aae
--- /dev/null
+++ b/debian/patches/debian/Only-start-logind-if-dbus-is-installed.patch
@@ -0,0 +1,24 @@
+From: Martin Pitt <martin.pitt@ubuntu.com>
+Date: Mon, 9 Feb 2015 10:53:43 +0100
+Subject: Only start logind if dbus is installed
+
+logind fails to start in environments without dbus, such as LXC containers or
+servers. Add a startup condition to avoid the very noisy startup failure.
+
+Part of #772700
+---
+ units/systemd-logind.service.in | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in
+index f131b60..952a07c 100644
+--- a/units/systemd-logind.service.in
++++ b/units/systemd-logind.service.in
+@@ -16,6 +16,7 @@ Documentation=man:org.freedesktop.login1(5)
+
+ Wants=user.slice modprobe@drm.service
+ After=nss-user-lookup.target user.slice modprobe@drm.service
++ConditionPathExists=/lib/systemd/system/dbus.service
+
+ # Ask for the dbus socket.
+ Wants=dbus.socket
diff --git a/debian/patches/debian/Re-enable-journal-forwarding-to-syslog.patch b/debian/patches/debian/Re-enable-journal-forwarding-to-syslog.patch
new file mode 100644
index 0000000..68b08a0
--- /dev/null
+++ b/debian/patches/debian/Re-enable-journal-forwarding-to-syslog.patch
@@ -0,0 +1,56 @@
+From: Martin Pitt <martin.pitt@ubuntu.com>
+Date: Fri, 28 Nov 2014 14:43:25 +0100
+Subject: Re-enable journal forwarding to syslog
+
+Revert upstream commit 46b131574fdd7d77 for now, until Debian's sysloggers
+can/do all read from the journal directly. See
+
+ http://lists.freedesktop.org/archives/systemd-devel/2014-November/025550.html
+
+for details. Once we grow a journal.conf.d/ directory, sysloggers can be moved
+to pulling from the journal one by one and disable forwarding again in such a
+conf.d snippet.
+---
+ man/journald.conf.xml | 2 +-
+ src/journal/journald-server.c | 1 +
+ src/journal/journald.conf | 2 +-
+ 3 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/man/journald.conf.xml b/man/journald.conf.xml
+index 959815a..2134a1d 100644
+--- a/man/journald.conf.xml
++++ b/man/journald.conf.xml
+@@ -344,7 +344,7 @@
+ traditional syslog daemon, to the kernel log buffer (kmsg), to the system console, or sent as wall
+ messages to all logged-in users. These options take boolean arguments. If forwarding to syslog is
+ enabled but nothing reads messages from the socket, forwarding to syslog has no effect. By default,
+- only forwarding to wall is enabled. These settings may be overridden at boot time with the kernel
++ only forwarding to syslog and wall is enabled. These settings may be overridden at boot time with the kernel
+ command line options <literal>systemd.journald.forward_to_syslog</literal>,
+ <literal>systemd.journald.forward_to_kmsg</literal>,
+ <literal>systemd.journald.forward_to_console</literal>, and
+diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
+index 10ebc3e..ce15766 100644
+--- a/src/journal/journald-server.c
++++ b/src/journal/journald-server.c
+@@ -2198,6 +2198,7 @@ int server_init(Server *s, const char *namespace) {
+ .ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL,
+ .ratelimit_burst = DEFAULT_RATE_LIMIT_BURST,
+
++ .forward_to_syslog = true,
+ .forward_to_wall = true,
+
+ .max_file_usec = DEFAULT_MAX_FILE_USEC,
+diff --git a/src/journal/journald.conf b/src/journal/journald.conf
+index 2e1aacd..d6cd5b1 100644
+--- a/src/journal/journald.conf
++++ b/src/journal/journald.conf
+@@ -29,7 +29,7 @@
+ #RuntimeMaxFiles=100
+ #MaxRetentionSec=
+ #MaxFileSec=1month
+-#ForwardToSyslog=no
++#ForwardToSyslog=yes
+ #ForwardToKMsg=no
+ #ForwardToConsole=no
+ #ForwardToWall=yes
diff --git a/debian/patches/debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch b/debian/patches/debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch
new file mode 100644
index 0000000..3346001
--- /dev/null
+++ b/debian/patches/debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch
@@ -0,0 +1,37 @@
+From: Martin Pitt <martin.pitt@ubuntu.com>
+Date: Mon, 27 Apr 2015 15:29:13 +0200
+Subject: Revert "core: one step back again,
+ for nspawn we actually can't wait for cgroups running empty since systemd
+ will get exactly zero notifications about it"
+
+This reverts commit 743970d2ea6d08aa7c7bff8220f6b7702f2b1db7.
+
+Bug-Debian: https://bugs.debian.org/784720
+Bug-Ubuntu: https://launchpad.net/bugs/1448259
+Bug-Fedora: https://bugzilla.redhat.com/show_bug.cgi?id=1141137
+---
+ src/core/unit.c | 11 +----------
+ 1 file changed, 1 insertion(+), 10 deletions(-)
+
+diff --git a/src/core/unit.c b/src/core/unit.c
+index 45a417a..811e038 100644
+--- a/src/core/unit.c
++++ b/src/core/unit.c
+@@ -5106,16 +5106,7 @@ int unit_kill_context(
+
+ } else if (r > 0) {
+
+- /* FIXME: For now, on the legacy hierarchy, we will not wait for the cgroup members to die if
+- * we are running in a container or if this is a delegation unit, simply because cgroup
+- * notification is unreliable in these cases. It doesn't work at all in containers, and outside
+- * of containers it can be confused easily by left-over directories in the cgroup — which
+- * however should not exist in non-delegated units. On the unified hierarchy that's different,
+- * there we get proper events. Hence rely on them. */
+-
+- if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0 ||
+- (detect_container() == 0 && !unit_cgroup_delegate(u)))
+- wait_for_exit = true;
++ wait_for_exit = true;
+
+ if (send_sighup) {
+ set_free(pid_set);
diff --git a/debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch b/debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch
new file mode 100644
index 0000000..7ed358b
--- /dev/null
+++ b/debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch
@@ -0,0 +1,46 @@
+From: Martin Pitt <martin.pitt@ubuntu.com>
+Date: Sat, 27 Feb 2016 12:27:06 +0100
+Subject: Revert "core: set RLIMIT_CORE to unlimited by default"
+
+Partially revert commit 15a900327ab as this completely breaks core dumps
+without systemd-coredump. It's also contradicting core(8), and it's not
+systemd's place to redefine the kernel definitions of core files.
+
+Commit bdfd7b2c now honours the process' RLIMIT_CORE for systemd-coredump. This
+isn't what RLIMIT_CORE is supposed to do (it limits the size of the core
+*file*, but the kernel deliberately ignores it for piping), so set a static
+2^63 core size limit for systemd-coredump to go back to the previous behaviour
+(otherwise the change above would break systemd-coredump).
+
+Bug-Debian: https://bugs.debian.org/815020
+---
+ src/core/main.c | 2 --
+ sysctl.d/50-coredump.conf.in | 2 +-
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/src/core/main.c b/src/core/main.c
+index a280b75..5c2a73a 100644
+--- a/src/core/main.c
++++ b/src/core/main.c
+@@ -2669,8 +2669,6 @@ int main(int argc, char *argv[]) {
+ kernel_timestamp = DUAL_TIMESTAMP_NULL;
+ }
+
+- initialize_coredump(skip_setup);
+-
+ r = fixup_environment();
+ if (r < 0) {
+ log_emergency_errno(r, "Failed to fix up PID 1 environment: %m");
+diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in
+index 4338756..8e501c4 100644
+--- a/sysctl.d/50-coredump.conf.in
++++ b/sysctl.d/50-coredump.conf.in
+@@ -13,7 +13,7 @@
+ # the core dump.
+ #
+ # See systemd-coredump(8) and core(5).
+-kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %h
++kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t 9223372036854775808 %h
+
+ # Allow that 16 coredumps are dispatched in parallel by the kernel. We want to
+ # be able to collect process metadata from /proc/%P/ while processing
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@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@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@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/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch b/debian/patches/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch
new file mode 100644
index 0000000..659692f
--- /dev/null
+++ b/debian/patches/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch
@@ -0,0 +1,57 @@
+From: Nis Martensen <nis.martensen@web.de>
+Date: Tue, 19 Jan 2016 22:01:43 +0100
+Subject: Skip filesystem check if already done by the initramfs
+
+Newer versions of initramfs-tools already fsck and mount / and /usr in
+the initramfs. Skip the filesystem check in this case.
+
+Based on a previous patch by Michael Biebl <biebl@debian.org>.
+
+Closes: #782522
+Closes: #810748
+---
+ src/fstab-generator/fstab-generator.c | 11 ++++++++---
+ units/systemd-fsck-root.service.in | 1 +
+ 2 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
+index 15f5892..e38d9a9 100644
+--- a/src/fstab-generator/fstab-generator.c
++++ b/src/fstab-generator/fstab-generator.c
+@@ -357,6 +357,7 @@ static int add_mount(
+ _cleanup_strv_free_ char **wanted_by = NULL, **required_by = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
++ struct stat sb;
+
+ assert(what);
+ assert(where);
+@@ -434,9 +435,13 @@ static int add_mount(
+ return r;
+
+ if (passno != 0) {
+- r = generator_write_fsck_deps(f, dest, what, where, fstype);
+- if (r < 0)
+- return r;
++ if (streq(where, "/usr") && stat("/run/initramfs/fsck-usr", &sb) == 0)
++ ; /* skip /usr fsck if it has already been checked in the initramfs */
++ else {
++ r = generator_write_fsck_deps(f, dest, what, where, fstype);
++ if (r < 0)
++ return r;
++ }
+ }
+
+ r = generator_write_blockdev_dependency(f, what);
+diff --git a/units/systemd-fsck-root.service.in b/units/systemd-fsck-root.service.in
+index 1dce176..908d931 100644
+--- a/units/systemd-fsck-root.service.in
++++ b/units/systemd-fsck-root.service.in
+@@ -16,6 +16,7 @@ Before=local-fs.target shutdown.target
+ Wants=systemd-fsckd.socket
+ After=systemd-fsckd.socket
+ ConditionPathIsReadWrite=!/
++ConditionPathExists=!/run/initramfs/fsck-root
+
+ [Service]
+ Type=oneshot
diff --git a/debian/patches/debian/Use-Debian-specific-config-files.patch b/debian/patches/debian/Use-Debian-specific-config-files.patch
new file mode 100644
index 0000000..0c0a82a
--- /dev/null
+++ b/debian/patches/debian/Use-Debian-specific-config-files.patch
@@ -0,0 +1,459 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Thu, 18 Jul 2013 20:11:02 +0200
+Subject: Use Debian specific config files
+
+Use /etc/default/locale instead of /etc/locale.conf for locale settings.
+
+Use /etc/default/keyboard instead of /etc/X11/xorg.conf.d/00-keyboard.conf for
+keyboard configuration.
+
+Read/write /etc/timezone if /etc/localtime does not exist.
+---
+ src/basic/time-util.c | 34 ++++++--
+ src/core/locale-setup.c | 21 +++++
+ src/locale/keymap-util.c | 208 ++++++++++++++++++++++++-----------------------
+ src/timedate/timedated.c | 21 ++++-
+ 4 files changed, 173 insertions(+), 111 deletions(-)
+
+diff --git a/src/basic/time-util.c b/src/basic/time-util.c
+index 5318d63..fa3409d 100644
+--- a/src/basic/time-util.c
++++ b/src/basic/time-util.c
+@@ -1456,19 +1456,43 @@ int get_timezone(char **ret) {
+ const char *e;
+ char *z;
+ int r;
++ bool use_utc_fallback = false;
+
+ r = readlink_malloc("/etc/localtime", &t);
+- if (r == -ENOENT) {
+- /* If the symlink does not exist, assume "UTC", like glibc does*/
+- z = strdup("UTC");
++ if (r < 0) {
++ if (r == -ENOENT)
++ use_utc_fallback = true;
++ else if (r != -EINVAL)
++ return r; /* returns EINVAL if not a symlink */
++
++ r = read_one_line_file("/etc/timezone", &t);
++ if (r < 0) {
++ if (r != -ENOENT)
++ log_warning_errno(r, "Failed to read /etc/timezone: %m");
++
++ if (use_utc_fallback) {
++ /* If the /etc/localtime symlink does not exist and we failed
++ * to read /etc/timezone, assume "UTC", like glibc does. */
++ z = strdup("UTC");
++ if (!z)
++ return -ENOMEM;
++
++ *ret = z;
++ return 0;
++ }
++
++ return -EINVAL;
++ }
++
++ if (!timezone_is_valid(t, LOG_DEBUG))
++ return -EINVAL;
++ z = strdup(t);
+ if (!z)
+ return -ENOMEM;
+
+ *ret = z;
+ return 0;
+ }
+- if (r < 0)
+- return r; /* returns EINVAL if not a symlink */
+
+ e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
+ if (!e)
+diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
+index 64761dd..b4ea9e2 100644
+--- a/src/core/locale-setup.c
++++ b/src/core/locale-setup.c
+@@ -58,6 +58,27 @@ int locale_setup(char ***environment) {
+ log_warning_errno(r, "Failed to read /etc/locale.conf: %m");
+ }
+
++ if (r <= 0) {
++ r = parse_env_file(NULL, "/etc/default/locale",
++ "LANG", &variables[VARIABLE_LANG],
++ "LANGUAGE", &variables[VARIABLE_LANGUAGE],
++ "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
++ "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
++ "LC_TIME", &variables[VARIABLE_LC_TIME],
++ "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
++ "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
++ "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
++ "LC_PAPER", &variables[VARIABLE_LC_PAPER],
++ "LC_NAME", &variables[VARIABLE_LC_NAME],
++ "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
++ "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
++ "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
++ "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]);
++
++ if (r < 0 && r != -ENOENT)
++ log_warning_errno(r, "Failed to read /etc/default/locale: %m");
++ }
++
+ for (i = 0; i < _VARIABLE_LC_MAX; i++) {
+ char *s;
+
+diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c
+index 697133a..4e1fedb 100644
+--- a/src/locale/keymap-util.c
++++ b/src/locale/keymap-util.c
+@@ -95,6 +95,7 @@ void locale_simplify(char *locale[_VARIABLE_LC_MAX]) {
+ int locale_read_data(Context *c, sd_bus_message *m) {
+ struct stat st;
+ int r;
++ const char *path = "/etc/locale.conf";
+
+ /* Do not try to re-read the file within single bus operation. */
+ if (m) {
+@@ -105,7 +106,11 @@ int locale_read_data(Context *c, sd_bus_message *m) {
+ c->locale_cache = sd_bus_message_ref(m);
+ }
+
+- r = stat("/etc/locale.conf", &st);
++ r = stat(path, &st);
++ if (r < 0 && errno == ENOENT) {
++ path = "/etc/default/locale";
++ r = stat(path, &st);
++ }
+ if (r < 0 && errno != ENOENT)
+ return -errno;
+
+@@ -120,7 +125,7 @@ int locale_read_data(Context *c, sd_bus_message *m) {
+ c->locale_mtime = t;
+ context_free_locale(c);
+
+- r = parse_env_file(NULL, "/etc/locale.conf",
++ r = parse_env_file(NULL, path,
+ "LANG", &c->locale[VARIABLE_LANG],
+ "LANGUAGE", &c->locale[VARIABLE_LANGUAGE],
+ "LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE],
+@@ -201,8 +206,6 @@ int vconsole_read_data(Context *c, sd_bus_message *m) {
+ }
+
+ int x11_read_data(Context *c, sd_bus_message *m) {
+- _cleanup_fclose_ FILE *f = NULL;
+- bool in_section = false;
+ struct stat st;
+ usec_t t;
+ int r;
+@@ -216,7 +219,7 @@ int x11_read_data(Context *c, sd_bus_message *m) {
+ c->x11_cache = sd_bus_message_ref(m);
+ }
+
+- if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) {
++ if (stat("/etc/default/keyboard", &st) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+@@ -233,60 +236,14 @@ int x11_read_data(Context *c, sd_bus_message *m) {
+ c->x11_mtime = t;
+ context_free_x11(c);
+
+- f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
+- if (!f)
+- return -errno;
+-
+- for (;;) {
+- _cleanup_free_ char *line = NULL;
+- char *l;
+-
+- r = read_line(f, LONG_LINE_MAX, &line);
+- if (r < 0)
+- return r;
+- if (r == 0)
+- break;
+-
+- l = strstrip(line);
+- if (IN_SET(l[0], 0, '#'))
+- continue;
+-
+- if (in_section && first_word(l, "Option")) {
+- _cleanup_strv_free_ char **a = NULL;
+-
+- r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
+- if (r < 0)
+- return r;
+-
+- if (strv_length(a) == 3) {
+- char **p = NULL;
+-
+- if (streq(a[1], "XkbLayout"))
+- p = &c->x11_layout;
+- else if (streq(a[1], "XkbModel"))
+- p = &c->x11_model;
+- else if (streq(a[1], "XkbVariant"))
+- p = &c->x11_variant;
+- else if (streq(a[1], "XkbOptions"))
+- p = &c->x11_options;
+-
+- if (p)
+- free_and_replace(*p, a[2]);
+- }
+-
+- } else if (!in_section && first_word(l, "Section")) {
+- _cleanup_strv_free_ char **a = NULL;
++ r = parse_env_file(NULL, "/etc/default/keyboard",
++ "XKBMODEL", &c->x11_model,
++ "XKBLAYOUT", &c->x11_layout,
++ "XKBVARIANT", &c->x11_variant,
++ "XKBOPTIONS", &c->x11_options);
+
+- r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
+- if (r < 0)
+- return -ENOMEM;
+-
+- if (strv_length(a) == 2 && streq(a[1], "InputClass"))
+- in_section = true;
+-
+- } else if (in_section && first_word(l, "EndSection"))
+- in_section = false;
+- }
++ if (r < 0)
++ return r;
+
+ return 0;
+ }
+@@ -295,9 +252,18 @@ int locale_write_data(Context *c, char ***settings) {
+ _cleanup_strv_free_ char **l = NULL;
+ struct stat st;
+ int r, p;
++ const char *path = "/etc/locale.conf";
+
+ /* Set values will be returned as strv in *settings on success. */
+
++ r = load_env_file(NULL, path, &l);
++ if (r < 0 && r == -ENOENT) {
++ path = "/etc/default/locale";
++ r = load_env_file(NULL, path, &l);
++ }
++ if (r < 0 && r != -ENOENT)
++ return r;
++
+ for (p = 0; p < _VARIABLE_LC_MAX; p++) {
+ _cleanup_free_ char *t = NULL;
+ char **u;
+@@ -320,20 +286,20 @@ int locale_write_data(Context *c, char ***settings) {
+ }
+
+ if (strv_isempty(l)) {
+- if (unlink("/etc/locale.conf") < 0)
++ if (unlink(path) < 0)
+ return errno == ENOENT ? 0 : -errno;
+
+ c->locale_mtime = USEC_INFINITY;
+ return 0;
+ }
+
+- r = write_env_file_label("/etc/locale.conf", l);
++ r = write_env_file_label(path, l);
+ if (r < 0)
+ return r;
+
+ *settings = TAKE_PTR(l);
+
+- if (stat("/etc/locale.conf", &st) >= 0)
++ if (stat(path, &st) >= 0)
+ c->locale_mtime = timespec_load(&st.st_mtim);
+
+ return 0;
+@@ -401,68 +367,104 @@ int vconsole_write_data(Context *c) {
+ }
+
+ int x11_write_data(Context *c) {
+- _cleanup_fclose_ FILE *f = NULL;
+- _cleanup_free_ char *temp_path = NULL;
+ struct stat st;
+ int r;
++ char *t, **u, **l = NULL;
+
+- if (isempty(c->x11_layout) &&
+- isempty(c->x11_model) &&
+- isempty(c->x11_variant) &&
+- isempty(c->x11_options)) {
++ r = load_env_file(NULL, "/etc/default/keyboard", &l);
++ if (r < 0 && r != -ENOENT)
++ return r;
+
+- if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
+- return errno == ENOENT ? 0 : -errno;
++ /* This could perhaps be done more elegantly using an array
++ * like we do for the locale, instead of struct
++ */
++ if (isempty(c->x11_layout)) {
++ l = strv_env_unset(l, "XKBLAYOUT");
++ } else {
++ if (asprintf(&t, "XKBLAYOUT=%s", c->x11_layout) < 0) {
++ strv_free(l);
++ return -ENOMEM;
++ }
+
+- c->vc_mtime = USEC_INFINITY;
+- return 0;
++ u = strv_env_set(l, t);
++ free(t);
++ strv_free(l);
++
++ if (!u)
++ return -ENOMEM;
++
++ l = u;
+ }
+
+- (void) mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
+- r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
+- if (r < 0)
+- return r;
++ if (isempty(c->x11_model)) {
++ l = strv_env_unset(l, "XKBMODEL");
++ } else {
++ if (asprintf(&t, "XKBMODEL=%s", c->x11_model) < 0) {
++ strv_free(l);
++ return -ENOMEM;
++ }
+
+- (void) fchmod(fileno(f), 0644);
++ u = strv_env_set(l, t);
++ free(t);
++ strv_free(l);
+
+- fputs("# Written by systemd-localed(8), read by systemd-localed and Xorg. It's\n"
+- "# probably wise not to edit this file manually. Use localectl(1) to\n"
+- "# instruct systemd-localed to update it.\n"
+- "Section \"InputClass\"\n"
+- " Identifier \"system-keyboard\"\n"
+- " MatchIsKeyboard \"on\"\n", f);
++ if (!u)
++ return -ENOMEM;
++
++ l = u;
++ }
++
++ if (isempty(c->x11_variant)) {
++ l = strv_env_unset(l, "XKBVARIANT");
++ } else {
++ if (asprintf(&t, "XKBVARIANT=%s", c->x11_variant) < 0) {
++ strv_free(l);
++ return -ENOMEM;
++ }
+
+- if (!isempty(c->x11_layout))
+- fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
++ u = strv_env_set(l, t);
++ free(t);
++ strv_free(l);
+
+- if (!isempty(c->x11_model))
+- fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
++ if (!u)
++ return -ENOMEM;
+
+- if (!isempty(c->x11_variant))
+- fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
++ l = u;
++ }
+
+- if (!isempty(c->x11_options))
+- fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
++ if (isempty(c->x11_options)) {
++ l = strv_env_unset(l, "XKBOPTIONS");
++ } else {
++ if (asprintf(&t, "XKBOPTIONS=%s", c->x11_options) < 0) {
++ strv_free(l);
++ return -ENOMEM;
++ }
+
+- fputs("EndSection\n", f);
++ u = strv_env_set(l, t);
++ free(t);
++ strv_free(l);
+
+- r = fflush_sync_and_check(f);
+- if (r < 0)
+- goto fail;
++ if (!u)
++ return -ENOMEM;
+
+- if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
+- r = -errno;
+- goto fail;
++ l = u;
+ }
+
+- if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0)
+- c->x11_mtime = timespec_load(&st.st_mtim);
++ if (strv_isempty(l)) {
++ strv_free(l);
+
+- return 0;
++ if (unlink("/etc/default/keyboard") < 0)
++ return errno == ENOENT ? 0 : -errno;
+
+-fail:
+- if (temp_path)
+- (void) unlink(temp_path);
++ c->vc_mtime = USEC_INFINITY;
++ return 0;
++ }
++
++ r = write_env_file("/etc/default/keyboard", l);
++ strv_free(l);
++
++ if (r >= 0 && stat("/etc/default/keyboard", &st) >= 0)
++ c->x11_mtime = timespec_load(&st.st_mtim);
+
+ return r;
+ }
+diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
+index 8149fac..42c8277 100644
+--- a/src/timedate/timedated.c
++++ b/src/timedate/timedated.c
+@@ -283,6 +283,8 @@ static int context_read_data(Context *c) {
+ static int context_write_data_timezone(Context *c) {
+ _cleanup_free_ char *p = NULL;
+ const char *source;
++ int r = 0;
++ struct stat st;
+
+ assert(c);
+
+@@ -296,9 +298,12 @@ static int context_write_data_timezone(Context *c) {
+ if (access("/usr/share/zoneinfo/UTC", F_OK) < 0) {
+
+ if (unlink("/etc/localtime") < 0 && errno != ENOENT)
+- return -errno;
++ r = -errno;
+
+- return 0;
++ if (unlink("/etc/timezone") < 0 && errno != ENOENT)
++ r = -errno;
++
++ return r;
+ }
+
+ source = "../usr/share/zoneinfo/UTC";
+@@ -310,7 +315,17 @@ static int context_write_data_timezone(Context *c) {
+ source = p;
+ }
+
+- return symlink_atomic(source, "/etc/localtime");
++ r = symlink_atomic(source, "/etc/localtime");
++ if (r < 0)
++ return r;
++
++ if (stat("/etc/timezone", &st) == 0 && S_ISREG(st.st_mode)) {
++ r = write_string_file("/etc/timezone", c->zone, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
++ if (r < 0)
++ return r;
++ }
++
++ return 0;
+ }
+
+ static int context_write_data_local_rtc(Context *c) {
diff --git a/debian/patches/debian/deny-list-upstream-test-02-ppc64el.patch b/debian/patches/debian/deny-list-upstream-test-02-ppc64el.patch
new file mode 100644
index 0000000..ee49d27
--- /dev/null
+++ b/debian/patches/debian/deny-list-upstream-test-02-ppc64el.patch
@@ -0,0 +1,17 @@
+From: Dan Streetman <ddstreet@canonical.com>
+Date: Wed, 6 Nov 2019 09:14:54 -0500
+Subject: deny-list-upstream-test-02-ppc64el
+
+Bug: https://github.com/systemd/systemd/issues/11612
+---
+ test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el | 1 +
+ 1 file changed, 1 insertion(+)
+ create mode 100644 test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el
+
+diff --git a/test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el b/test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el
+new file mode 100644
+index 0000000..52877fc
+--- /dev/null
++++ b/test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el
+@@ -0,0 +1 @@
++# unknown reason for failing, tracked in https://github.com/systemd/systemd/issues/11612
diff --git a/debian/patches/debian/deny-list-upstream-test-25.patch b/debian/patches/debian/deny-list-upstream-test-25.patch
new file mode 100644
index 0000000..ec6c211
--- /dev/null
+++ b/debian/patches/debian/deny-list-upstream-test-25.patch
@@ -0,0 +1,17 @@
+From: Dan Streetman <ddstreet@canonical.com>
+Date: Wed, 6 Nov 2019 09:14:50 -0500
+Subject: deny-list-upstream-test-25
+
+Bug: https://github.com/systemd/systemd/issues/13973
+---
+ test/TEST-25-IMPORT/deny-list-upstream-ci | 1 +
+ 1 file changed, 1 insertion(+)
+ create mode 100644 test/TEST-25-IMPORT/deny-list-upstream-ci
+
+diff --git a/test/TEST-25-IMPORT/deny-list-upstream-ci b/test/TEST-25-IMPORT/deny-list-upstream-ci
+new file mode 100644
+index 0000000..47a5f15
+--- /dev/null
++++ b/test/TEST-25-IMPORT/deny-list-upstream-ci
+@@ -0,0 +1 @@
++# unknown failure; tracked in https://github.com/systemd/systemd/issues/13973
diff --git a/debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch b/debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch
new file mode 100644
index 0000000..6e61355
--- /dev/null
+++ b/debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch
@@ -0,0 +1,1065 @@
+From: Didier Roche <didrocks@ubuntu.com>
+Date: Fri, 22 May 2015 13:04:38 +0200
+Subject: fsckd daemon for inter-fsckd communication
+
+Global logic:
+Add systemd-fsckd multiplexer which accepts multiple (via systemd-fsck's
+/run/systemd/fsck.progress socket) fsck instances to connect to it and sends
+progress report. systemd-fsckd then computes and writes to /dev/console the
+number of devices currently being checked and the minimum fsck progress.
+
+Plymouth and user interaction:
+Forward the progress to plymouth and support canellation of in progress fsck.
+Try to connect and send to plymouth (if running) some checked report progress,
+using direct plymouth protocole.
+
+Update message is the following:
+fsckd:<num_devices>:<progress>:<string>
+* num_devices corresponds to the current number of devices being checked (int)
+* progress corresponds to the current minimum percentage of all devices being
+ checked (float, from 0 to 100)
+* string is a translated message ready to be displayed by the plymouth theme
+ displaying the information above. It can be overridden by plymouth themes
+ supporting i18n.
+
+Grab in fsckd plymouth watch key Control+C, and propagate this cancel request
+to systemd-fsck which will terminate fsck.
+
+Send a message to signal to user what key we are grabbing for fsck cancel.
+
+Message is: fsckd-cancel-msg:<string>
+Where string is a translated string ready to be displayed by the plymouth theme
+indicating that Control+C can be used to cancel current checks. It can be
+overridden (matching only fsckd-cancel-msg prefix) for themes supporting i18n.
+
+Misc:
+systemd-fsckd stops on idle when no fsck is connected.
+Add man page explaining the plymouth theme protocol, usage of the daemon
+as well as the socket activation part. Adapt existing fsck man page.
+
+Note that fsckd had lived in the upstream tree for a while, but was removed.
+More information at
+http://lists.freedesktop.org/archives/systemd-devel/2015-April/030175.html
+-
+---
+ man/rules/meson.build | 1 +
+ man/systemd-fsckd.service.xml | 162 +++++++++
+ meson.build | 9 +
+ po/POTFILES.in | 1 +
+ src/fsckd/fsckd.c | 699 +++++++++++++++++++++++++++++++++++++
+ units/meson.build | 2 +
+ units/systemd-fsck-root.service.in | 2 +
+ units/systemd-fsck@.service.in | 3 +-
+ units/systemd-fsckd.service.in | 17 +
+ units/systemd-fsckd.socket | 15 +
+ 10 files changed, 910 insertions(+), 1 deletion(-)
+ create mode 100644 man/systemd-fsckd.service.xml
+ create mode 100644 src/fsckd/fsckd.c
+ create mode 100644 units/systemd-fsckd.service.in
+ create mode 100644 units/systemd-fsckd.socket
+
+diff --git a/man/rules/meson.build b/man/rules/meson.build
+index cacbbd7..285f5b8 100644
+--- a/man/rules/meson.build
++++ b/man/rules/meson.build
+@@ -840,6 +840,7 @@ manpages = [
+ '8',
+ ['systemd-fsck', 'systemd-fsck-root.service'],
+ ''],
++ ['systemd-fsckd.service', '8', ['systemd-fsckd.socket', 'systemd-fsckd'], ''],
+ ['systemd-fstab-generator', '8', [], ''],
+ ['systemd-getty-generator', '8', [], ''],
+ ['systemd-gpt-auto-generator', '8', [], 'HAVE_BLKID'],
+diff --git a/man/systemd-fsckd.service.xml b/man/systemd-fsckd.service.xml
+new file mode 100644
+index 0000000..b7ad58d
+--- /dev/null
++++ b/man/systemd-fsckd.service.xml
+@@ -0,0 +1,162 @@
++<?xml version="1.0"?>
++<!--*-nxml-*-->
++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
++<!--
++ This file is part of systemd.
++
++ Copyright 2015 Canonical
++
++ systemd is free software; you can redistribute it and/or modify it
++ under the terms of the GNU Lesser General Public License as published by
++ the Free Software Foundation; either version 2.1 of the License, or
++ (at your option) any later version.
++
++ systemd is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public License
++ along with systemd; If not, see <http://www.gnu.org/licenses/>.
++-->
++<refentry id="systemd-fsckd.service" xmlns:xi="http://www.w3.org/2001/XInclude">
++
++ <refentryinfo>
++ <title>systemd-fsckd.service</title>
++ <productname>systemd</productname>
++
++ <authorgroup>
++ <author>
++ <contrib>Developer</contrib>
++ <firstname>Didier</firstname>
++ <surname>Roche</surname>
++ <email>didrocks@ubuntu.com</email>
++ </author>
++ </authorgroup>
++ </refentryinfo>
++
++ <refmeta>
++ <refentrytitle>systemd-fsckd.service</refentrytitle>
++ <manvolnum>8</manvolnum>
++ </refmeta>
++
++ <refnamediv>
++ <refname>systemd-fsckd.service</refname>
++ <refname>systemd-fsckd.socket</refname>
++ <refname>systemd-fsckd</refname>
++ <refpurpose>File system check progress reporting</refpurpose>
++ </refnamediv>
++
++ <refsynopsisdiv>
++ <para><filename>systemd-fsckd.service</filename></para>
++ <para><filename>systemd-fsckd.socket</filename></para>
++ <para><filename>/usr/lib/systemd/systemd-fsckd</filename></para>
++ </refsynopsisdiv>
++
++ <refsect1>
++ <title>Description</title>
++
++ <para><filename>systemd-fsckd.service</filename> is a service responsible
++ for receiving file system check progress, and communicating some
++ consolidated data to console and plymouth (if running). It also handles
++ possible check cancellations.</para>
++
++ <para><command>systemd-fsckd</command> receives messages about file
++ system check progress from <command>fsck</command> through an
++ UNIX domain socket. It can display the progress of the least advanced
++ fsck as well as the total number of devices being checked in parallel
++ to the console. It will also send progress messages to plymouth.
++ Both the raw data and translated messages are sent, so compiled
++ plymouth themes can use the raw data to display custom messages, and
++ scripted themes, not supporting i18n, can display the translated
++ versions.</para>
++
++ <para><command>systemd-fsckd</command> will instruct plymouth to grab
++ Control+C keypresses. When the key is pressed, running checks will be
++ terminated. It will also cancel any newly connected fsck instances for
++ the lifetime of <filename>systemd-fsckd</filename>.</para>
++ </refsect1>
++
++ <refsect1>
++ <title>Protocol for communication with plymouth</title>
++
++ <para><filename>systemd-fsckd</filename> passes the
++ following messages to the theme:</para>
++
++ <para>Progress update, sent as a plymouth update message:
++ <literal>fsckd:&lt;num_devices&gt;:&lt;progress&gt;:&lt;string&gt;</literal>
++ <variablelist>
++ <varlistentry>
++ <term><literal>&lt;num_devices&gt;</literal></term>
++ <listitem><para>the current number of devices
++ being checked (int)</para></listitem>
++ </varlistentry>
++ <varlistentry>
++ <term><literal>&lt;progress&gt;</literal></term>
++ <listitem><para>the current minimum percentage of
++ all devices being checking (float, from 0 to 100)</para></listitem>
++ </varlistentry>
++ <varlistentry>
++ <term><literal>&lt;string&gt;</literal></term>
++ <listitem><para>a translated message ready to be displayed
++ by the plymouth theme displaying the data above. It can be overridden
++ by themes supporting i18n.</para></listitem>
++ </varlistentry>
++ </variablelist>
++ </para>
++
++ <para>Cancel message, sent as a traditional plymouth message:
++ <literal>fsckd-cancel-msg:&lt;string&gt;</literal>
++ <variablelist>
++ <varlistentry>
++ <term><literal>&lt;strings&gt;</literal></term>
++ <listitem><para>a translated string ready to be displayed
++ by the plymouth theme indicating that Control+C can be used to cancel
++ current checks. It can be overridden (matching only
++ <literal>fsckd-cancel-msg</literal> prefix)
++ by themes supporting i18n.</para></listitem>
++ </varlistentry>
++ </variablelist>
++ </para>
++ </refsect1>
++
++ <refsect1>
++ <title>Options</title>
++
++ <para>The following options are understood:</para>
++
++ <variablelist>
++ <xi:include href="standard-options.xml" xpointer="help" />
++ <xi:include href="standard-options.xml" xpointer="version" />
++ </variablelist>
++
++ </refsect1>
++
++ <refsect1>
++ <title>Exit status</title>
++
++ <para>On success, 0 is returned, a non-zero failure
++ code otherwise. Note that the daemon stays idle for
++ a while to accept new <filename>fsck</filename>
++ connections before exiting.</para>
++ </refsect1>
++
++ <refsect1>
++ <title>See Also</title>
++ <para>
++ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd-fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd-quotacheck.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.cramfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.ext4</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.fat</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.hfsplus</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.minix</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.ntfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
++ </para>
++ </refsect1>
++
++</refentry>
+diff --git a/meson.build b/meson.build
+index cf93f38..e33b729 100644
+--- a/meson.build
++++ b/meson.build
+@@ -2895,6 +2895,15 @@ executable(
+ install : true,
+ install_dir : rootlibexecdir)
+
++executable(
++ 'systemd-fsckd',
++ 'src/fsckd/fsckd.c',
++ include_directories : includes,
++ link_with : [libshared],
++ install_rpath : rootlibexecdir,
++ install : true,
++ install_dir : rootlibexecdir)
++
+ executable(
+ 'systemd-sleep',
+ 'src/sleep/sleep.c',
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 0346a19..5ea2444 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -10,3 +10,4 @@ src/portable/org.freedesktop.portable1.policy
+ src/resolve/org.freedesktop.resolve1.policy
+ src/timedate/org.freedesktop.timedate1.policy
+ src/core/dbus-unit.c
++src/fsckd/fsckd.c
+diff --git a/src/fsckd/fsckd.c b/src/fsckd/fsckd.c
+new file mode 100644
+index 0000000..4af8e45
+--- /dev/null
++++ b/src/fsckd/fsckd.c
+@@ -0,0 +1,699 @@
++/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
++
++/***
++ This file is part of systemd.
++
++ Copyright 2015 Canonical
++
++ Author:
++ Didier Roche <didrocks@ubuntu.com>
++
++ systemd is free software; you can redistribute it and/or modify it
++ under the terms of the GNU Lesser General Public License as published by
++ the Free Software Foundation; either version 2.1 of the License, or
++ (at your option) any later version.
++
++ systemd is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public License
++ along with systemd; If not, see <http://www.gnu.org/licenses/>.
++***/
++
++#include <getopt.h>
++#include <errno.h>
++#include <libintl.h>
++#include <math.h>
++#include <stdbool.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <sys/un.h>
++#include <unistd.h>
++
++#include "sd-daemon.h"
++#include "build.h"
++#include "def.h"
++#include "sd-event.h"
++#include "log.h"
++#include "list.h"
++#include "macro.h"
++#include "socket-netlink.h"
++#include "socket-util.h"
++#include "fd-util.h"
++#include "string-util.h"
++#include "io-util.h"
++#include "util.h"
++#include "alloc-util.h"
++#include "locale-util.h"
++
++#define FSCKD_SOCKET_PATH "/run/systemd/fsck.progress"
++#define IDLE_TIME_SECONDS 30
++#define PLYMOUTH_REQUEST_KEY "K\2\2\3"
++#define CLIENTS_MAX 128
++
++struct Manager;
++
++typedef struct Client {
++ struct Manager *manager;
++ char *device_name;
++ /* device id refers to "fd <fd>" until it gets a name as "device_name" */
++ char *device_id;
++
++ pid_t fsck_pid;
++ FILE *fsck_f;
++
++ size_t cur;
++ size_t max;
++ int pass;
++
++ double percent;
++
++ bool cancelled;
++ bool bad_input;
++
++ sd_event_source *event_source;
++
++ LIST_FIELDS(struct Client, clients);
++} Client;
++
++typedef struct Manager {
++ sd_event *event;
++
++ LIST_HEAD(Client, clients);
++ unsigned n_clients;
++
++ size_t clear;
++
++ int connection_fd;
++ sd_event_source *connection_event_source;
++
++ bool show_status_console;
++
++ double percent;
++ int numdevices;
++
++ int plymouth_fd;
++ sd_event_source *plymouth_event_source;
++ bool plymouth_cancel_sent;
++
++ bool cancel_requested;
++} Manager;
++
++static void client_free(Client *c);
++static void manager_free(Manager *m);
++
++DEFINE_TRIVIAL_CLEANUP_FUNC(Client*, client_free);
++DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
++
++static bool plymouth_running(void) {
++ return access("/run/plymouth/pid", F_OK) >= 0;
++}
++
++static int manager_write_console(Manager *m, const char *message) {
++ _cleanup_fclose_ FILE *console = NULL;
++ int l;
++ size_t j;
++
++ assert(m);
++
++ if (!m->show_status_console)
++ return 0;
++
++ /* Nothing to display, and nothing to clear: return now. */
++ if (message == NULL && m->clear == 0) {
++ return 0;
++ }
++
++ /* Reduce the SAK window by opening and closing console on every request */
++ console = fopen("/dev/console", "we");
++ if (!console)
++ return -errno;
++
++ if (message) {
++ fprintf(console, "\r%s\r%n", message, &l);
++ if (m->clear < (size_t)l)
++ m->clear = (size_t)l;
++ } else {
++ fputc('\r', console);
++ for (j = 0; j < m->clear; j++)
++ fputc(' ', console);
++ fputc('\r', console);
++ }
++ fflush(console);
++
++ return 0;
++}
++
++static double compute_percent(int pass, size_t cur, size_t max) {
++ /* Values stolen from e2fsck */
++
++ static const double pass_table[] = {
++ 0, 70, 90, 92, 95, 100
++ };
++
++ if (pass <= 0)
++ return 0.0;
++
++ if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
++ return 100.0;
++
++ return pass_table[pass-1] +
++ (pass_table[pass] - pass_table[pass-1]) *
++ (double) cur / max;
++}
++
++static int client_request_cancel(Client *c) {
++ assert(c);
++
++ if (c->cancelled)
++ return 0;
++
++ log_info("Request to cancel fsck for %s from fsckd", c->device_id);
++ if (kill(c->fsck_pid, SIGTERM) < 0) {
++ /* ignore the error and consider that cancel was sent if fsck just exited */
++ if (errno != ESRCH)
++ return log_error_errno(errno, "Cannot send cancel to fsck for %s: %m", c->device_id);
++ }
++
++ c->cancelled = true;
++ return 1;
++}
++
++static void client_free(Client *c) {
++ assert(c);
++
++ if (c->manager) {
++ LIST_REMOVE(clients, c->manager->clients, c);
++ c->manager->n_clients--;
++ }
++
++ sd_event_source_unref(c->event_source);
++ fclose(c->fsck_f);
++ if (c->device_name)
++ free(c->device_name);
++ if (c->device_id)
++ free(c->device_id);
++ free(c);
++}
++
++static void manager_disconnect_plymouth(Manager *m) {
++ assert(m);
++
++ m->plymouth_event_source = sd_event_source_unref(m->plymouth_event_source);
++ m->plymouth_fd = safe_close(m->plymouth_fd);
++ m->plymouth_cancel_sent = false;
++}
++
++static int manager_plymouth_feedback_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
++ Manager *m = userdata;
++ Client *current;
++ char buffer[6];
++ ssize_t l;
++
++ assert(m);
++
++ l = read(m->plymouth_fd, buffer, sizeof(buffer));
++ if (l < 0) {
++ log_warning_errno(errno, "Got error while reading from plymouth: %m");
++ manager_disconnect_plymouth(m);
++ return -errno;
++ }
++ if (l == 0) {
++ manager_disconnect_plymouth(m);
++ return 0;
++ }
++
++ if (l > 1 && buffer[0] == '\15')
++ log_error("Message update to plymouth wasn't delivered successfully");
++
++ /* the only answer support type we requested is a key interruption */
++ if (l > 2 && buffer[0] == '\2' && buffer[5] == '\3') {
++ m->cancel_requested = true;
++
++ /* cancel all connected clients */
++ LIST_FOREACH(clients, current, m->clients)
++ client_request_cancel(current);
++ }
++
++ return 0;
++}
++
++static int manager_connect_plymouth(Manager *m) {
++ union sockaddr_union sa = PLYMOUTH_SOCKET;
++ int r;
++
++ if (!plymouth_running())
++ return 0;
++
++ /* try to connect or reconnect if sending a message */
++ if (m->plymouth_fd >= 0)
++ return 1;
++
++ m->plymouth_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
++ if (m->plymouth_fd < 0)
++ return log_warning_errno(errno, "Connection to plymouth socket failed: %m");
++
++ if (connect(m->plymouth_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
++ r = log_warning_errno(errno, "Couldn't connect to plymouth: %m");
++ goto fail;
++ }
++
++ r = sd_event_add_io(m->event, &m->plymouth_event_source, m->plymouth_fd, EPOLLIN, manager_plymouth_feedback_handler, m);
++ if (r < 0) {
++ log_warning_errno(r, "Can't listen to plymouth socket: %m");
++ goto fail;
++ }
++
++ return 1;
++
++fail:
++ manager_disconnect_plymouth(m);
++ return r;
++}
++
++static int plymouth_send_message(int plymouth_fd, const char *message, bool update) {
++ _cleanup_free_ char *packet = NULL;
++ int n;
++ char mode = 'M';
++
++ if (update)
++ mode = 'U';
++
++ if (asprintf(&packet, "%c\002%c%s%n", mode, (int) (strlen(message) + 1), message, &n) < 0)
++ return log_oom();
++
++ return loop_write(plymouth_fd, packet, n + 1, true);
++}
++
++static int manager_send_plymouth_message(Manager *m, const char *message) {
++ const char *plymouth_cancel_message = NULL, *l10n_cancel_message = NULL;
++ int r;
++
++ r = manager_connect_plymouth(m);
++ if (r < 0)
++ return r;
++ /* 0 means that plymouth isn't running, do not send any message yet */
++ else if (r == 0)
++ return 0;
++
++ if (!m->plymouth_cancel_sent) {
++
++ /* Indicate to plymouth that we listen to Ctrl+C */
++ r = loop_write(m->plymouth_fd, PLYMOUTH_REQUEST_KEY, sizeof(PLYMOUTH_REQUEST_KEY), true);
++ if (r < 0)
++ return log_warning_errno(r, "Can't send to plymouth cancel key: %m");
++
++ m->plymouth_cancel_sent = true;
++
++ l10n_cancel_message = _("Press Ctrl+C to cancel all filesystem checks in progress");
++ plymouth_cancel_message = strjoina("fsckd-cancel-msg:", l10n_cancel_message);
++
++ r = plymouth_send_message(m->plymouth_fd, plymouth_cancel_message, false);
++ if (r < 0)
++ log_warning_errno(r, "Can't send filesystem cancel message to plymouth: %m");
++
++ } else if (m->numdevices == 0) {
++
++ m->plymouth_cancel_sent = false;
++
++ r = plymouth_send_message(m->plymouth_fd, "", false);
++ if (r < 0)
++ log_warning_errno(r, "Can't clear plymouth filesystem cancel message: %m");
++ }
++
++ r = plymouth_send_message(m->plymouth_fd, message, true);
++ if (r < 0)
++ return log_warning_errno(r, "Couldn't send \"%s\" to plymouth: %m", message);
++
++ return 0;
++}
++
++static int manager_update_global_progress(Manager *m) {
++ Client *current = NULL;
++ _cleanup_free_ char *console_message = NULL;
++ _cleanup_free_ char *fsck_message = NULL;
++ int current_numdevices = 0, r;
++ double current_percent = 100;
++
++ /* get the overall percentage */
++ LIST_FOREACH(clients, current, m->clients) {
++ current_numdevices++;
++
++ /* right now, we only keep the minimum % of all fsckd processes. We could in the future trying to be
++ linear, but max changes and corresponds to the pass. We have all the informations into fsckd
++ already if we can treat that in a smarter way. */
++ current_percent = MIN(current_percent, current->percent);
++ }
++
++ /* update if there is anything user-visible to update */
++ if (fabs(current_percent - m->percent) > 0.001 || current_numdevices != m->numdevices) {
++ m->numdevices = current_numdevices;
++ m->percent = current_percent;
++
++ if (asprintf(&console_message,
++ ngettext("Checking in progress on %d disk (%3.1f%% complete)",
++ "Checking in progress on %d disks (%3.1f%% complete)", m->numdevices),
++ m->numdevices, m->percent) < 0)
++ return -ENOMEM;
++
++ if (asprintf(&fsck_message, "fsckd:%d:%3.1f:%s", m->numdevices, m->percent, console_message) < 0)
++ return -ENOMEM;
++
++ r = manager_write_console(m, console_message);
++ if (r < 0)
++ return r;
++
++ /* try to connect to plymouth and send message */
++ r = manager_send_plymouth_message(m, fsck_message);
++ if (r < 0)
++ return r;
++ }
++ return 0;
++}
++
++static int client_progress_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
++ Client *client = userdata;
++ char line[LINE_MAX];
++ Manager *m;
++
++ assert(client);
++ m = client->manager;
++
++ /* check first if we need to cancel this client */
++ if (m->cancel_requested)
++ client_request_cancel(client);
++
++ while (fgets(line, sizeof(line), client->fsck_f) != NULL) {
++ int pass;
++ size_t cur, max;
++ _cleanup_free_ char *device = NULL, *old_device_id = NULL;
++
++ if (sscanf(line, "%i %zu %zu %ms", &pass, &cur, &max, &device) == 4) {
++ if (!client->device_name) {
++ client->device_name = strdup(device);
++ if (!client->device_name) {
++ log_oom();
++ continue;
++ }
++ old_device_id = client->device_id;
++ client->device_id = strdup(device);
++ if (!client->device_id) {
++ log_oom();
++ client->device_id = old_device_id;
++ old_device_id = NULL;
++ continue;
++ }
++ }
++ client->pass = pass;
++ client->cur = cur;
++ client->max = max;
++ client->bad_input = false;
++ client->percent = compute_percent(client->pass, client->cur, client->max);
++ log_debug("Getting progress for %s (%zu, %zu, %d) : %3.1f%%", client->device_id,
++ client->cur, client->max, client->pass, client->percent);
++ } else {
++ if (errno == ENOMEM) {
++ log_oom();
++ continue;
++ }
++
++ /* if previous input was already garbage, kick it off from progress report */
++ if (client->bad_input) {
++ log_warning("Closing connection on incorrect input of fsck connection for %s", client->device_id);
++ client_free(client);
++ manager_update_global_progress(m);
++ return 0;
++ }
++ client->bad_input = true;
++ }
++
++ }
++
++ if (feof(client->fsck_f)) {
++ log_debug("Fsck client %s disconnected", client->device_id);
++ client_free(client);
++ }
++
++ manager_update_global_progress(m);
++ return 0;
++}
++
++static int manager_new_connection_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
++ _cleanup_(client_freep) Client *c = NULL;
++ _cleanup_close_ int new_fsck_fd = -1;
++ _cleanup_fclose_ FILE *new_fsck_f = NULL;
++ struct ucred ucred = {};
++ Manager *m = userdata;
++ int r;
++
++ assert(m);
++
++ /* Initialize and list new clients */
++ new_fsck_fd = accept4(m->connection_fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
++ if (new_fsck_fd < 0) {
++ log_error_errno(errno, "Couldn't accept a new connection: %m");
++ return 0;
++ }
++
++ if (m->n_clients >= CLIENTS_MAX) {
++ log_error("Too many clients, refusing connection.");
++ return 0;
++ }
++
++
++ new_fsck_f = fdopen(new_fsck_fd, "r");
++ if (!new_fsck_f) {
++ log_error_errno(errno, "Couldn't fdopen new connection for fd %d: %m", new_fsck_fd);
++ return 0;
++ }
++ new_fsck_fd = -1;
++
++ r = getpeercred(fileno(new_fsck_f), &ucred);
++ if (r < 0) {
++ log_error_errno(r, "Couldn't get credentials for fsck: %m");
++ return 0;
++ }
++
++ c = new0(Client, 1);
++ if (!c) {
++ log_oom();
++ return 0;
++ }
++
++ c->fsck_pid = ucred.pid;
++ c->fsck_f = new_fsck_f;
++ new_fsck_f = NULL;
++
++ if (asprintf(&(c->device_id), "fd %d", fileno(c->fsck_f)) < 0) {
++ log_oom();
++ return 0;
++ }
++
++ r = sd_event_add_io(m->event, &c->event_source, fileno(c->fsck_f), EPOLLIN, client_progress_handler, c);
++ if (r < 0) {
++ log_oom();
++ return 0;
++ }
++
++ LIST_PREPEND(clients, m->clients, c);
++ m->n_clients++;
++ c->manager = m;
++
++ log_debug("New fsck client connected: %s", c->device_id);
++
++ /* only request the client to cancel now in case the request is dropped by the client (chance to recancel) */
++ if (m->cancel_requested)
++ client_request_cancel(c);
++
++ c = NULL;
++ return 0;
++}
++
++static void manager_free(Manager *m) {
++ if (!m)
++ return;
++
++ /* clear last line */
++ manager_write_console(m, NULL);
++
++ sd_event_source_unref(m->connection_event_source);
++ safe_close(m->connection_fd);
++
++ while (m->clients)
++ client_free(m->clients);
++
++ manager_disconnect_plymouth(m);
++
++ sd_event_unref(m->event);
++
++ free(m);
++}
++
++static int manager_new(Manager **ret, int fd) {
++ _cleanup_(manager_freep) Manager *m = NULL;
++ int r;
++
++ assert(ret);
++
++ m = new0(Manager, 1);
++ if (!m)
++ return -ENOMEM;
++
++ m->plymouth_fd = -1;
++ m->connection_fd = fd;
++ m->percent = 100;
++
++ r = sd_event_default(&m->event);
++ if (r < 0)
++ return r;
++
++ if (access("/run/systemd/show-status", F_OK) >= 0)
++ m->show_status_console = true;
++
++ r = sd_event_add_io(m->event, &m->connection_event_source, fd, EPOLLIN, manager_new_connection_handler, m);
++ if (r < 0)
++ return r;
++
++ *ret = m;
++ m = NULL;
++
++ return 0;
++}
++
++static int run_event_loop_with_timeout(Manager *m, usec_t timeout) {
++ int r, code;
++ sd_event *e = m->event;
++
++ assert(e);
++
++ for (;;) {
++ r = sd_event_get_state(e);
++ if (r < 0)
++ return r;
++ if (r == SD_EVENT_FINISHED)
++ break;
++
++ r = sd_event_run(e, timeout);
++ if (r < 0)
++ return r;
++
++ /* Exit if we reached the idle timeout and no more clients are
++ connected. If there is still an fsck process running but
++ simply slow to send us progress updates, exiting would mean
++ that this fsck process receives SIGPIPE resulting in an
++ aborted file system check. */
++ if (r == 0 && m->n_clients == 0) {
++ sd_event_exit(e, 0);
++ break;
++ }
++ }
++
++ r = sd_event_get_exit_code(e, &code);
++ if (r < 0)
++ return r;
++
++ return code;
++}
++
++static void help(void) {
++ printf("%s [OPTIONS...]\n\n"
++ "Capture fsck progress and forward one stream to plymouth\n\n"
++ " -h --help Show this help\n"
++ " --version Show package version\n",
++ program_invocation_short_name);
++}
++
++static int parse_argv(int argc, char *argv[]) {
++
++ enum {
++ ARG_VERSION = 0x100,
++ ARG_ROOT,
++ };
++
++ static const struct option options[] = {
++ { "help", no_argument, NULL, 'h' },
++ { "version", no_argument, NULL, ARG_VERSION },
++ {}
++ };
++
++ int c;
++
++ assert(argc >= 0);
++ assert(argv);
++
++ while ((c = getopt_long(argc, argv, "hv", options, NULL)) >= 0)
++ switch (c) {
++
++ case 'h':
++ help();
++ return 0;
++
++ case ARG_VERSION:
++ version();
++ return 0;
++
++ case '?':
++ return -EINVAL;
++
++ default:
++ assert_not_reached("Unhandled option");
++ }
++
++ if (optind < argc) {
++ log_error("Extraneous arguments");
++ return -EINVAL;
++ }
++
++ return 1;
++}
++
++int main(int argc, char *argv[]) {
++ _cleanup_(manager_freep) Manager *m = NULL;
++ int fd = -1;
++ int r, n;
++
++ log_set_target(LOG_TARGET_AUTO);
++ log_parse_environment();
++ log_open();
++ init_gettext();
++
++ r = parse_argv(argc, argv);
++ if (r <= 0)
++ goto finish;
++
++ n = sd_listen_fds(0);
++ if (n > 1) {
++ log_error("Too many file descriptors received.");
++ r = -EINVAL;
++ goto finish;
++ } else if (n == 1)
++ fd = SD_LISTEN_FDS_START + 0;
++ else {
++ fd = make_socket_fd(LOG_DEBUG, FSCKD_SOCKET_PATH, SOCK_STREAM, SOCK_CLOEXEC);
++ if (fd < 0) {
++ r = log_error_errno(fd, "Couldn't create listening socket fd on %s: %m", FSCKD_SOCKET_PATH);
++ goto finish;
++ }
++ }
++
++ r = manager_new(&m, fd);
++ if (r < 0) {
++ log_error_errno(r, "Failed to allocate manager: %m");
++ goto finish;
++ }
++
++ r = run_event_loop_with_timeout(m, IDLE_TIME_SECONDS * USEC_PER_SEC);
++ if (r < 0) {
++ log_error_errno(r, "Failed to run event loop: %m");
++ goto finish;
++ }
++
++ sd_event_get_exit_code(m->event, &r);
++
++finish:
++ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
++}
+diff --git a/units/meson.build b/units/meson.build
+index ba60eb7..95d7eda 100644
+--- a/units/meson.build
++++ b/units/meson.build
+@@ -103,6 +103,7 @@ units = [
+ ['systemd-exit.service', ''],
+ ['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
+ 'sysinit.target.wants/'],
++ ['systemd-fsckd.socket', ''],
+ ['systemd-halt.service', ''],
+ ['systemd-homed-activate.service', 'ENABLE_HOMED'],
+ ['systemd-initctl.socket', 'HAVE_SYSV_COMPAT',
+@@ -174,6 +175,7 @@ in_units = [
+ ['systemd-pstore.service', 'ENABLE_PSTORE'],
+ ['systemd-fsck-root.service', ''],
+ ['systemd-fsck@.service', ''],
++ ['systemd-fsckd.service', ''],
+ ['systemd-hibernate-resume@.service', 'ENABLE_HIBERNATE'],
+ ['systemd-hibernate.service', 'ENABLE_HIBERNATE'],
+ ['systemd-hybrid-sleep.service', 'ENABLE_HIBERNATE'],
+diff --git a/units/systemd-fsck-root.service.in b/units/systemd-fsck-root.service.in
+index c4a2948..1dce176 100644
+--- a/units/systemd-fsck-root.service.in
++++ b/units/systemd-fsck-root.service.in
+@@ -13,6 +13,8 @@ Documentation=man:systemd-fsck-root.service(8)
+ DefaultDependencies=no
+ Conflicts=shutdown.target
+ Before=local-fs.target shutdown.target
++Wants=systemd-fsckd.socket
++After=systemd-fsckd.socket
+ ConditionPathIsReadWrite=!/
+
+ [Service]
+diff --git a/units/systemd-fsck@.service.in b/units/systemd-fsck@.service.in
+index 6d9c9ab..48d5f29 100644
+--- a/units/systemd-fsck@.service.in
++++ b/units/systemd-fsck@.service.in
+@@ -13,7 +13,8 @@ Documentation=man:systemd-fsck@.service(8)
+ DefaultDependencies=no
+ BindsTo=%i.device
+ Conflicts=shutdown.target
+-After=%i.device systemd-fsck-root.service local-fs-pre.target
++Wants=systemd-fsckd.socket
++After=%i.device systemd-fsck-root.service local-fs-pre.target systemd-fsckd.socket
+ Before=systemd-quotacheck.service shutdown.target
+
+ [Service]
+diff --git a/units/systemd-fsckd.service.in b/units/systemd-fsckd.service.in
+new file mode 100644
+index 0000000..9c7ed51
+--- /dev/null
++++ b/units/systemd-fsckd.service.in
+@@ -0,0 +1,17 @@
++# This file is part of systemd.
++#
++# systemd is free software; you can redistribute it and/or modify it
++# under the terms of the GNU Lesser General Public License as published by
++# the Free Software Foundation; either version 2.1 of the License, or
++# (at your option) any later version.
++
++[Unit]
++Description=File System Check Daemon to report status
++Documentation=man:systemd-fsckd.service(8)
++DefaultDependencies=no
++Requires=systemd-fsckd.socket
++Before=shutdown.target
++
++[Service]
++ExecStart=@rootlibexecdir@/systemd-fsckd
++StandardOutput=journal+console
+diff --git a/units/systemd-fsckd.socket b/units/systemd-fsckd.socket
+new file mode 100644
+index 0000000..61fec97
+--- /dev/null
++++ b/units/systemd-fsckd.socket
+@@ -0,0 +1,15 @@
++# This file is part of systemd.
++#
++# systemd is free software; you can redistribute it and/or modify it
++# under the terms of the GNU Lesser General Public License as published by
++# the Free Software Foundation; either version 2.1 of the License, or
++# (at your option) any later version.
++
++[Unit]
++Description=fsck to fsckd communication Socket
++Documentation=man:systemd-fsckd.service(8) man:systemd-fsck@.service(8) man:systemd-fsck-root.service(8)
++DefaultDependencies=no
++
++[Socket]
++ListenStream=/run/systemd/fsck.progress
++SocketMode=0600
diff --git a/debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch b/debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch
new file mode 100644
index 0000000..68fca31
--- /dev/null
+++ b/debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch
@@ -0,0 +1,52 @@
+From: Ioanna Alifieraki <ioanna-maria.alifieraki@canonical.com>
+Date: Thu, 17 Dec 2020 14:52:07 +0000
+Subject: systemctl: do not shutdown immediately on scheduled shutdown
+
+When, for whatever reason, a scheduled shutdown fails to be set, systemd
+will proceed with immediate shutdown without allowing the user to react.
+This is counterintuitive because when a scheduled shutdown is issued,
+it means the user wants to shutdown at a specified time in the future,
+not immediately. This patch prevents the immediate shutdown and informs
+the user that no action will be taken.
+
+Fixes: #17575
+---
+ src/systemctl/systemctl-compat-halt.c | 8 ++++----
+ src/systemctl/systemctl-logind.c | 3 ++-
+ 2 files changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c
+index 8e41bd6..239a780 100644
+--- a/src/systemctl/systemctl-compat-halt.c
++++ b/src/systemctl/systemctl-compat-halt.c
+@@ -149,11 +149,11 @@ int halt_main(void) {
+ if (r < 0)
+ return r;
+
+- /* Delayed shutdown requested, and was successful */
+- if (arg_when > 0 && logind_schedule_shutdown() == 0)
+- return 0;
++ /* Delayed shutdown requested */
++ if (arg_when > 0)
++ return logind_schedule_shutdown();
+
+- /* No delay, or logind failed or is not at all available */
++ /* No delay, or logind is not at all available */
+ if (geteuid() != 0) {
+ if (arg_dry_run || arg_force > 0) {
+ (void) must_be_root();
+diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c
+index 405f12a..fd0b143 100644
+--- a/src/systemctl/systemctl-logind.c
++++ b/src/systemctl/systemctl-logind.c
+@@ -315,8 +315,9 @@ int logind_schedule_shutdown(void) {
+ (void) logind_set_wall_message();
+
+ r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
++ /* logind fails, cannot schedule reboot, do nothing */
+ if (r < 0)
+- return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
++ return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, no action will be taken: %s", bus_error_message(&error, r));
+
+ if (!arg_quiet)
+ log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", log_action, format_timestamp_style(date, sizeof(date), arg_when, arg_timestamp_style));
diff --git a/debian/patches/debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch b/debian/patches/debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch
new file mode 100644
index 0000000..0a48308
--- /dev/null
+++ b/debian/patches/debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch
@@ -0,0 +1,131 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Mon, 18 Jan 2021 13:33:10 +0100
+Subject: test: disable DnsmasqClientTest.test_resolved_etc_hosts in
+ networkd-test.py
+
+This test appears to be flaky.
+
+See: #979716
+---
+ test/networkd-test.py | 108 +++++++++++++++++++++++++-------------------------
+ 1 file changed, 54 insertions(+), 54 deletions(-)
+
+diff --git a/test/networkd-test.py b/test/networkd-test.py
+index 8496ec8..ca158bf 100755
+--- a/test/networkd-test.py
++++ b/test/networkd-test.py
+@@ -674,60 +674,60 @@ Domains= ~company ~lab''')
+ self.assertRegex(general_log, 'query.*megasearch.net')
+ self.assertNotIn('megasearch.net', vpn_log)
+
+- def test_resolved_etc_hosts(self):
+- '''resolved queries to /etc/hosts'''
+-
+- # FIXME: -t MX query fails with enabled DNSSEC (even when using
+- # the known negative trust anchor .internal instead of .example.com)
+- conf = '/run/systemd/resolved.conf.d/test-disable-dnssec.conf'
+- os.makedirs(os.path.dirname(conf), exist_ok=True)
+- with open(conf, 'w') as f:
+- f.write('[Resolve]\nDNSSEC=no\nLLMNR=no\nMulticastDNS=no\n')
+- self.addCleanup(os.remove, conf)
+-
+- # create /etc/hosts bind mount which resolves my.example.com for IPv4
+- hosts = os.path.join(self.workdir, 'hosts')
+- with open(hosts, 'w') as f:
+- f.write('172.16.99.99 my.example.com\n')
+- subprocess.check_call(['mount', '--bind', hosts, '/etc/hosts'])
+- self.addCleanup(subprocess.call, ['umount', '/etc/hosts'])
+- subprocess.check_call(['systemctl', 'stop', 'systemd-resolved.service'])
+-
+- # note: different IPv4 address here, so that it's easy to tell apart
+- # what resolved the query
+- self.create_iface(dnsmasq_opts=['--host-record=my.example.com,172.16.99.1,2600::99:99',
+- '--host-record=other.example.com,172.16.0.42,2600::42',
+- '--mx-host=example.com,mail.example.com'],
+- ipv6=True)
+- self.do_test(coldplug=None, ipv6=True)
+-
+- try:
+- # family specific queries
+- out = subprocess.check_output(['resolvectl', 'query', '-4', 'my.example.com'])
+- self.assertIn(b'my.example.com: 172.16.99.99', out)
+- # we don't expect an IPv6 answer; if /etc/hosts has any IP address,
+- # it's considered a sufficient source
+- self.assertNotEqual(subprocess.call(['resolvectl', 'query', '-6', 'my.example.com']), 0)
+- # "any family" query; IPv4 should come from /etc/hosts
+- out = subprocess.check_output(['resolvectl', 'query', 'my.example.com'])
+- self.assertIn(b'my.example.com: 172.16.99.99', out)
+- # IP → name lookup; again, takes the /etc/hosts one
+- out = subprocess.check_output(['resolvectl', 'query', '172.16.99.99'])
+- self.assertIn(b'172.16.99.99: my.example.com', out)
+-
+- # non-address RRs should fall back to DNS
+- out = subprocess.check_output(['resolvectl', 'query', '--type=MX', 'example.com'])
+- self.assertIn(b'example.com IN MX 1 mail.example.com', out)
+-
+- # other domains query DNS
+- out = subprocess.check_output(['resolvectl', 'query', 'other.example.com'])
+- self.assertIn(b'172.16.0.42', out)
+- out = subprocess.check_output(['resolvectl', 'query', '172.16.0.42'])
+- self.assertIn(b'172.16.0.42: other.example.com', out)
+- except (AssertionError, subprocess.CalledProcessError):
+- self.show_journal('systemd-resolved.service')
+- self.print_server_log()
+- raise
++# def test_resolved_etc_hosts(self):
++# '''resolved queries to /etc/hosts'''
++#
++# # FIXME: -t MX query fails with enabled DNSSEC (even when using
++# # the known negative trust anchor .internal instead of .example.com)
++# conf = '/run/systemd/resolved.conf.d/test-disable-dnssec.conf'
++# os.makedirs(os.path.dirname(conf), exist_ok=True)
++# with open(conf, 'w') as f:
++# f.write('[Resolve]\nDNSSEC=no\nLLMNR=no\nMulticastDNS=no\n')
++# self.addCleanup(os.remove, conf)
++#
++# # create /etc/hosts bind mount which resolves my.example.com for IPv4
++# hosts = os.path.join(self.workdir, 'hosts')
++# with open(hosts, 'w') as f:
++# f.write('172.16.99.99 my.example.com\n')
++# subprocess.check_call(['mount', '--bind', hosts, '/etc/hosts'])
++# self.addCleanup(subprocess.call, ['umount', '/etc/hosts'])
++# subprocess.check_call(['systemctl', 'stop', 'systemd-resolved.service'])
++#
++# # note: different IPv4 address here, so that it's easy to tell apart
++# # what resolved the query
++# self.create_iface(dnsmasq_opts=['--host-record=my.example.com,172.16.99.1,2600::99:99',
++# '--host-record=other.example.com,172.16.0.42,2600::42',
++# '--mx-host=example.com,mail.example.com'],
++# ipv6=True)
++# self.do_test(coldplug=None, ipv6=True)
++#
++# try:
++# # family specific queries
++# out = subprocess.check_output(['resolvectl', 'query', '-4', 'my.example.com'])
++# self.assertIn(b'my.example.com: 172.16.99.99', out)
++# # we don't expect an IPv6 answer; if /etc/hosts has any IP address,
++# # it's considered a sufficient source
++# self.assertNotEqual(subprocess.call(['resolvectl', 'query', '-6', 'my.example.com']), 0)
++# # "any family" query; IPv4 should come from /etc/hosts
++# out = subprocess.check_output(['resolvectl', 'query', 'my.example.com'])
++# self.assertIn(b'my.example.com: 172.16.99.99', out)
++# # IP → name lookup; again, takes the /etc/hosts one
++# out = subprocess.check_output(['resolvectl', 'query', '172.16.99.99'])
++# self.assertIn(b'172.16.99.99: my.example.com', out)
++#
++# # non-address RRs should fall back to DNS
++# out = subprocess.check_output(['resolvectl', 'query', '--type=MX', 'example.com'])
++# self.assertIn(b'example.com IN MX 1 mail.example.com', out)
++#
++# # other domains query DNS
++# out = subprocess.check_output(['resolvectl', 'query', 'other.example.com'])
++# self.assertIn(b'172.16.0.42', out)
++# out = subprocess.check_output(['resolvectl', 'query', '172.16.0.42'])
++# self.assertIn(b'172.16.0.42: other.example.com', out)
++# except (AssertionError, subprocess.CalledProcessError):
++# self.show_journal('systemd-resolved.service')
++# self.print_server_log()
++# raise
+
+ def test_transient_hostname(self):
+ '''networkd sets transient hostname from DHCP'''
diff --git a/debian/patches/debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch b/debian/patches/debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch
new file mode 100644
index 0000000..f5432e8
--- /dev/null
+++ b/debian/patches/debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch
@@ -0,0 +1,25 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Tue, 19 Nov 2019 09:10:23 +0100
+Subject: udev: drop SystemCallArchitectures=native from systemd-udevd.service
+
+We can't really control what helper programs are run from other udev
+rules. E.g. running i386 binaries under amd64 is a valid use case and
+should not trigger a SIGSYS failure.
+
+Closes: #869719
+---
+ units/systemd-udevd.service.in | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in
+index 225eac2..f541ff6 100644
+--- a/units/systemd-udevd.service.in
++++ b/units/systemd-udevd.service.in
+@@ -35,7 +35,6 @@ MemoryDenyWriteExecute=yes
+ RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
+ RestrictRealtime=yes
+ RestrictSUIDSGID=yes
+-SystemCallArchitectures=native
+ LockPersonality=yes
+ IPAddressDeny=any
+ @SERVICE_WATCHDOG@
diff --git a/debian/patches/localed-Run-locale-gen-if-available-to-generate-missing-l.patch b/debian/patches/localed-Run-locale-gen-if-available-to-generate-missing-l.patch
new file mode 100644
index 0000000..6752b9d
--- /dev/null
+++ b/debian/patches/localed-Run-locale-gen-if-available-to-generate-missing-l.patch
@@ -0,0 +1,448 @@
+From: Matthias Klumpp <matthias@tenstral.net>
+Date: Fri, 8 Jan 2021 23:59:38 +0100
+Subject: localed: Run locale-gen if available to generate missing locale
+
+This change improves integration with distributions using locale-gen to
+generate missing locale on-demand, like Debian-based distributions
+(Debian/Ubuntu/PureOS/Tanglu/...) and Arch Linux.
+We only ever enable new locales for generation, and never disable them.
+Furthermore, we only generate UTF-8 locale.
+
+This feature is only used if explicitly enabled at compile-time, and
+will also be inert at runtime if the locale-gen binary is missing.
+
+(cherry picked from commit 8f20232fcb52dbe6255f3df6101fc057af90bcfa)
+---
+ meson.build | 8 ++
+ meson_options.txt | 2 +
+ src/locale/keymap-util.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++
+ src/locale/keymap-util.h | 4 +
+ src/locale/localectl.c | 6 +-
+ src/locale/localed.c | 59 ++++++++++++-
+ 6 files changed, 286 insertions(+), 4 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 580964c..cf93f38 100644
+--- a/meson.build
++++ b/meson.build
+@@ -833,6 +833,14 @@ if default_locale == ''
+ endif
+ conf.set_quoted('SYSTEMD_DEFAULT_LOCALE', default_locale)
+
++localegen_path = get_option('localegen-path')
++have = false
++if localegen_path != ''
++ conf.set_quoted('LOCALEGEN_PATH', localegen_path)
++ have = true
++endif
++conf.set10('HAVE_LOCALEGEN', have)
++
+ conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
+
+ service_watchdog = get_option('service-watchdog')
+diff --git a/meson_options.txt b/meson_options.txt
+index 2435cce..4ffdf7f 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -238,6 +238,8 @@ option('gshadow', type : 'boolean',
+ description : 'support for shadow group')
+ option('default-locale', type : 'string', value : '',
+ description : 'default locale used when /etc/locale.conf does not exist')
++option('localegen-path', type : 'string', value : '',
++ description : 'absolute path to the locale-gen binary in case the system is using locale-gen')
+ option('service-watchdog', type : 'string', value : '3min',
+ description : 'default watchdog setting for systemd services')
+
+diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c
+index cb8153f..697133a 100644
+--- a/src/locale/keymap-util.c
++++ b/src/locale/keymap-util.c
+@@ -6,18 +6,21 @@
+ #include <unistd.h>
+
+ #include "bus-polkit.h"
++#include "copy.h"
+ #include "env-file-label.h"
+ #include "env-file.h"
+ #include "env-util.h"
+ #include "fd-util.h"
+ #include "fileio-label.h"
+ #include "fileio.h"
++#include "fs-util.h"
+ #include "kbd-util.h"
+ #include "keymap-util.h"
+ #include "locale-util.h"
+ #include "macro.h"
+ #include "mkdir.h"
+ #include "nulstr-util.h"
++#include "process-util.h"
+ #include "string-util.h"
+ #include "strv.h"
+ #include "tmpfile-util.h"
+@@ -780,3 +783,211 @@ int x11_convert_to_vconsole(Context *c) {
+
+ return modified;
+ }
++
++bool locale_gen_check_available(void) {
++#if HAVE_LOCALEGEN
++ if (access(LOCALEGEN_PATH, X_OK) < 0) {
++ if (errno != ENOENT)
++ log_warning_errno(errno, "Unable to determine whether " LOCALEGEN_PATH " exists and is executable, assuming it is not: %m");
++ return false;
++ }
++ if (access("/etc/locale.gen", F_OK) < 0) {
++ if (errno != ENOENT)
++ log_warning_errno(errno, "Unable to determine whether /etc/locale.gen exists, assuming it does not: %m");
++ return false;
++ }
++ return true;
++#else
++ return false;
++#endif
++}
++
++#if HAVE_LOCALEGEN
++static bool locale_encoding_is_utf8_or_unspecified(const char *locale) {
++ const char *c = strchr(locale, '.');
++ return !c || strcaseeq(c, ".UTF-8") || strcasestr(locale, ".UTF-8@");
++}
++
++static int locale_gen_locale_supported(const char *locale_entry) {
++ /* Returns an error valus <= 0 if the locale-gen entry is invalid or unsupported,
++ * 1 in case the locale entry is valid, and -EOPNOTSUPP specifically in case
++ * the distributor has not provided us with a SUPPORTED file to check
++ * locale for validity. */
++
++ _cleanup_fclose_ FILE *f = NULL;
++ int r;
++
++ assert(locale_entry);
++
++ /* Locale templates without country code are never supported */
++ if (!strstr(locale_entry, "_"))
++ return -EINVAL;
++
++ f = fopen("/usr/share/i18n/SUPPORTED", "re");
++ if (!f) {
++ if (errno == ENOENT)
++ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
++ "Unable to check validity of locale entry %s: /usr/share/i18n/SUPPORTED does not exist",
++ locale_entry);
++ return -errno;
++ }
++
++ for (;;) {
++ _cleanup_free_ char *line = NULL;
++
++ r = read_line(f, LONG_LINE_MAX, &line);
++ if (r < 0)
++ return log_debug_errno(r, "Failed to read /usr/share/i18n/SUPPORTED: %m");
++ if (r == 0)
++ return 0;
++
++ line = strstrip(line);
++ if (strcaseeq_ptr(line, locale_entry))
++ return 1;
++ }
++}
++#endif
++
++int locale_gen_enable_locale(const char *locale) {
++#if HAVE_LOCALEGEN
++ _cleanup_fclose_ FILE *fr = NULL, *fw = NULL;
++ _cleanup_(unlink_and_freep) char *temp_path = NULL;
++ _cleanup_free_ char *locale_entry = NULL;
++ bool locale_enabled = false, first_line = false;
++ bool write_new = false;
++ int r;
++
++ if (isempty(locale))
++ return 0;
++
++ if (locale_encoding_is_utf8_or_unspecified(locale)) {
++ locale_entry = strjoin(locale, " UTF-8");
++ if (!locale_entry)
++ return -ENOMEM;
++ } else
++ return -ENOEXEC; /* We do not process non-UTF-8 locale */
++
++ r = locale_gen_locale_supported(locale_entry);
++ if (r == 0)
++ return -EINVAL;
++ if (r < 0 && r != -EOPNOTSUPP)
++ return r;
++
++ fr = fopen("/etc/locale.gen", "re");
++ if (!fr) {
++ if (errno != ENOENT)
++ return -errno;
++ write_new = true;
++ }
++
++ r = fopen_temporary("/etc/locale.gen", &fw, &temp_path);
++ if (r < 0)
++ return r;
++
++ if (write_new)
++ (void) fchmod(fileno(fw), 0644);
++ else {
++ /* apply mode & xattrs of the original file to new file */
++ r = copy_access(fileno(fr), fileno(fw));
++ if (r < 0)
++ return r;
++ r = copy_xattr(fileno(fr), fileno(fw));
++ if (r < 0)
++ return r;
++ }
++
++ if (!write_new) {
++ /* The config file ends with a line break, which we do not want to include before potentially appending a new locale
++ * instead of uncommenting an existing line. By prepending linebreaks, we can avoid buffering this file but can still write
++ * a nice config file without empty lines */
++ first_line = true;
++ for (;;) {
++ _cleanup_free_ char *line = NULL;
++ char *line_locale;
++
++ r = read_line(fr, LONG_LINE_MAX, &line);
++ if (r < 0)
++ return r;
++ if (r == 0)
++ break;
++
++ if (locale_enabled) {
++ /* Just complete writing the file if the new locale was already enabled */
++ if (!first_line)
++ fputc('\n', fw);
++ fputs(line, fw);
++ first_line = false;
++ continue;
++ }
++
++ line = strstrip(line);
++ if (isempty(line)) {
++ fputc('\n', fw);
++ first_line = false;
++ continue;
++ }
++
++ line_locale = line;
++ if (line_locale[0] == '#')
++ line_locale = strstrip(line_locale + 1);
++ else if (strcaseeq_ptr(line_locale, locale_entry))
++ return 0; /* the file already had our locale activated, so skip updating it */
++
++ if (strcaseeq_ptr(line_locale, locale_entry)) {
++ /* Uncomment existing line for new locale */
++ if (!first_line)
++ fputc('\n', fw);
++ fputs(locale_entry, fw);
++ locale_enabled = true;
++ first_line = false;
++ continue;
++ }
++
++ /* The line was not for the locale we want to enable, just copy it */
++ if (!first_line)
++ fputc('\n', fw);
++ fputs(line, fw);
++ first_line = false;
++ }
++ }
++
++ /* Add locale to enable to the end of the file if it was not found as commented line */
++ if (!locale_enabled) {
++ if (!write_new)
++ fputc('\n', fw);
++ fputs(locale_entry, fw);
++ }
++ fputc('\n', fw);
++
++ r = fflush_sync_and_check(fw);
++ if (r < 0)
++ return r;
++
++ if (rename(temp_path, "/etc/locale.gen") < 0)
++ return -errno;
++ temp_path = mfree(temp_path);
++
++ return 0;
++#else
++ return -EOPNOTSUPP;
++#endif
++}
++
++int locale_gen_run(void) {
++#if HAVE_LOCALEGEN
++ pid_t pid;
++ int r;
++
++ r = safe_fork("(sd-localegen)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, &pid);
++ if (r < 0)
++ return r;
++ if (r == 0) {
++ execl(LOCALEGEN_PATH, LOCALEGEN_PATH, NULL);
++ _exit(EXIT_FAILURE);
++ }
++
++ return 0;
++#else
++ return -EOPNOTSUPP;
++#endif
++}
+diff --git a/src/locale/keymap-util.h b/src/locale/keymap-util.h
+index 4997647..c087dbc 100644
+--- a/src/locale/keymap-util.h
++++ b/src/locale/keymap-util.h
+@@ -42,3 +42,7 @@ int x11_convert_to_vconsole(Context *c);
+ int x11_write_data(Context *c);
+ void locale_simplify(char *locale[_VARIABLE_LC_MAX]);
+ int locale_write_data(Context *c, char ***settings);
++
++bool locale_gen_check_available(void);
++int locale_gen_enable_locale(const char *locale);
++int locale_gen_run(void);
+diff --git a/src/locale/localectl.c b/src/locale/localectl.c
+index 7d2e887..7d3d3f8 100644
+--- a/src/locale/localectl.c
++++ b/src/locale/localectl.c
+@@ -26,6 +26,9 @@
+ #include "verbs.h"
+ #include "virt.h"
+
++/* Enough time for locale-gen to finish server-side (in case it is in use) */
++#define LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
++
+ static PagerFlags arg_pager_flags = 0;
+ static bool arg_ask_password = true;
+ static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
+@@ -176,7 +179,8 @@ static int set_locale(int argc, char **argv, void *userdata) {
+ if (r < 0)
+ return bus_log_create_error(r);
+
+- r = sd_bus_call(bus, m, 0, &error, NULL);
++ /* We use a longer timeout for the method call in case localed is running locale-gen */
++ r = sd_bus_call(bus, m, LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
+
+diff --git a/src/locale/localed.c b/src/locale/localed.c
+index 736dacd..12073bd 100644
+--- a/src/locale/localed.c
++++ b/src/locale/localed.c
+@@ -262,6 +262,7 @@ static int property_get_xkb(
+ static int process_locale_list_item(
+ const char *assignment,
+ char *new_locale[static _VARIABLE_LC_MAX],
++ bool use_localegen,
+ sd_bus_error *error) {
+
+ assert(assignment);
+@@ -283,7 +284,7 @@ static int process_locale_list_item(
+
+ if (!locale_is_valid(e))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e);
+- if (locale_is_installed(e) <= 0)
++ if (!use_localegen && locale_is_installed(e) <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e);
+ if (new_locale[p])
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name);
+@@ -298,6 +299,47 @@ static int process_locale_list_item(
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment);
+ }
+
++static int locale_gen_process_locale(char *new_locale[static _VARIABLE_LC_MAX],
++ sd_bus_error *error) {
++ int r;
++ assert(new_locale);
++
++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
++ if (p == VARIABLE_LANGUAGE)
++ continue;
++ if (isempty(new_locale[p]))
++ continue;
++ if (locale_is_installed(new_locale[p]))
++ continue;
++
++ r = locale_gen_enable_locale(new_locale[p]);
++ if (r == -ENOEXEC) {
++ log_error_errno(r, "Refused to enable locale for generation: %m");
++ return sd_bus_error_setf(error,
++ SD_BUS_ERROR_INVALID_ARGS,
++ "Specified locale is not installed and non-UTF-8 locale will not be auto-generated: %s",
++ new_locale[p]);
++ } else if (r == -EINVAL) {
++ log_error_errno(r, "Failed to enable invalid locale %s for generation.", new_locale[p]);
++ return sd_bus_error_setf(error,
++ SD_BUS_ERROR_INVALID_ARGS,
++ "Can not enable locale generation for invalid locale: %s",
++ new_locale[p]);
++ } else if (r < 0) {
++ log_error_errno(r, "Failed to enable locale for generation: %m");
++ return sd_bus_error_set_errnof(error, r, "Failed to enable locale generation: %m");
++ }
++
++ r = locale_gen_run();
++ if (r < 0) {
++ log_error_errno(r, "Failed to generate locale: %m");
++ return sd_bus_error_set_errnof(error, r, "Failed to generate locale: %m");
++ }
++ }
++
++ return 0;
++}
++
+ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {};
+ _cleanup_strv_free_ char **settings = NULL, **l = NULL;
+@@ -305,6 +347,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
+ bool modified = false;
+ int interactive, r;
+ char **i;
++ bool use_localegen;
+
+ assert(m);
+ assert(c);
+@@ -317,11 +360,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
+ if (r < 0)
+ return r;
+
++ use_localegen = locale_gen_check_available();
++
+ /* If single locale without variable name is provided, then we assume it is LANG=. */
+ if (strv_length(l) == 1 && !strchr(l[0], '=')) {
+ if (!locale_is_valid(l[0]))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]);
+- if (locale_is_installed(l[0]) <= 0)
++ if (!use_localegen && locale_is_installed(l[0]) <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]);
+
+ new_locale[VARIABLE_LANG] = strdup(l[0]);
+@@ -333,7 +378,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
+
+ /* Check whether a variable is valid */
+ STRV_FOREACH(i, l) {
+- r = process_locale_list_item(*i, new_locale, error);
++ r = process_locale_list_item(*i, new_locale, use_localegen, error);
+ if (r < 0)
+ return r;
+ }
+@@ -392,9 +437,17 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
++ /* Generate locale in case it is missing and the system is using locale-gen */
++ if (use_localegen) {
++ r = locale_gen_process_locale(new_locale, error);
++ if (r < 0)
++ return r;
++ }
++
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
+ free_and_replace(c->locale[p], new_locale[p]);
+
++ /* Write locale configuration */
+ r = locale_write_data(c, &settings);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set locale: %m");
diff --git a/debian/patches/logind-fix-getting-property-OnExternalPower-via-D-Bus.patch b/debian/patches/logind-fix-getting-property-OnExternalPower-via-D-Bus.patch
new file mode 100644
index 0000000..509dfc7
--- /dev/null
+++ b/debian/patches/logind-fix-getting-property-OnExternalPower-via-D-Bus.patch
@@ -0,0 +1,36 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Wed, 12 Oct 2022 11:07:57 +0200
+Subject: logind: fix getting property OnExternalPower via D-Bus
+
+The BUS_DEFINE_PROPERTY_GET_GLOBAL macro requires a value as third
+argument, so we need to call manager_is_on_external_power(). Otherwise
+the function pointer is interpreted as a boolean and always returns
+true:
+
+```
+$ busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager OnExternalPower
+b true
+$ /lib/systemd/systemd-ac-power --verbose
+no
+```
+
+Thanks: Helmut Grohne <helmut@subdivi.de>
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1021644
+(cherry picked from commit 63168cb517a556b2f4f175b365f5a4b4c7e85150)
+---
+ src/login/logind-dbus.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
+index b95af1a..cf2be79 100644
+--- a/src/login/logind-dbus.c
++++ b/src/login/logind-dbus.c
+@@ -352,7 +352,7 @@ static int property_get_scheduled_shutdown(
+ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
+ static BUS_DEFINE_PROPERTY_GET(property_get_docked, "b", Manager, manager_is_docked_or_external_displays);
+ static BUS_DEFINE_PROPERTY_GET(property_get_lid_closed, "b", Manager, manager_is_lid_closed);
+-static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_on_external_power, "b", manager_is_on_external_power);
++static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_on_external_power, "b", manager_is_on_external_power());
+ static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_compat_user_tasks_max, "t", CGROUP_LIMIT_MAX);
+ static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "t", Hashmap *, (uint64_t) hashmap_size);
+
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 0000000..50ae974
--- /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 0000000..965e925
--- /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 0000000..e9cd9dd
--- /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/machined-varlink-fix-double-free.patch b/debian/patches/machined-varlink-fix-double-free.patch
new file mode 100644
index 0000000..80ccaca
--- /dev/null
+++ b/debian/patches/machined-varlink-fix-double-free.patch
@@ -0,0 +1,22 @@
+From: David Tardon <dtardon@redhat.com>
+Date: Mon, 2 Aug 2021 13:31:04 +0200
+Subject: machined-varlink: fix double free
+
+Fixes: #18599
+---
+ src/machine/machined-varlink.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c
+index 2d6c199..8c2e456 100644
+--- a/src/machine/machined-varlink.c
++++ b/src/machine/machined-varlink.c
+@@ -297,7 +297,7 @@ static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char
+ desc = mfree(desc);
+
+ *ret_gid = converted_gid;
+- *ret_description = desc;
++ *ret_description = TAKE_PTR(desc);
+ return 0;
+ }
+
diff --git a/debian/patches/network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch b/debian/patches/network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch
new file mode 100644
index 0000000..055c598
--- /dev/null
+++ b/debian/patches/network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch
@@ -0,0 +1,86 @@
+From: "Kevin P. Fleming" <kevin@km6g.us>
+Date: Sat, 6 Feb 2021 10:58:43 -0500
+Subject: network: Delay addition of IPv6 Proxy NDP addresses
+
+Setting of IPv6 Proxy NDP addresses must be done at the same
+time as static addresses, static routes, and other link attributes
+that must be configured when the link is up. Doing this ensures
+that they are reconfigured on the link if the link goes down
+and returns to service.
+
+(cherry picked from commit 12f7469bbe0142d7f360a29ca2b407ce7f5ff096)
+
+Fixes https://github.com/systemd/systemd-stable/issues/89
+
+(cherry picked from commit d5ea028e46673ef627843e90c3d01ebac8fe0e62)
+---
+ src/network/networkd-address.c | 11 +++++++++++
+ src/network/networkd-link.c | 5 -----
+ 2 files changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
+index 961b248..ef47af4 100644
+--- a/src/network/networkd-address.c
++++ b/src/network/networkd-address.c
+@@ -9,6 +9,7 @@
+ #include "netlink-util.h"
+ #include "networkd-address-pool.h"
+ #include "networkd-address.h"
++#include "networkd-ipv6-proxy-ndp.h"
+ #include "networkd-manager.h"
+ #include "networkd-network.h"
+ #include "parse-util.h"
+@@ -903,6 +904,7 @@ int address_configure(
+ static int static_address_ready_callback(Address *address) {
+ Address *a;
+ Link *link;
++ int r;
+
+ assert(address);
+ assert(address->link);
+@@ -927,6 +929,10 @@ static int static_address_ready_callback(Address *address) {
+
+ link->addresses_ready = true;
+
++ r = link_set_ipv6_proxy_ndp_addresses(link);
++ if (r < 0)
++ return r;
++
+ return link_set_routes(link);
+ }
+
+@@ -1046,6 +1052,11 @@ int link_set_addresses(Link *link) {
+ if (link->address_messages == 0) {
+ link->addresses_configured = true;
+ link->addresses_ready = true;
++
++ r = link_set_ipv6_proxy_ndp_addresses(link);
++ if (r < 0)
++ return r;
++
+ r = link_set_routes(link);
+ if (r < 0)
+ return r;
+diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
+index 8120343..e8a7223 100644
+--- a/src/network/networkd-link.c
++++ b/src/network/networkd-link.c
+@@ -28,7 +28,6 @@
+ #include "networkd-dhcp6.h"
+ #include "networkd-fdb.h"
+ #include "networkd-ipv4ll.h"
+-#include "networkd-ipv6-proxy-ndp.h"
+ #include "networkd-link-bus.h"
+ #include "networkd-link.h"
+ #include "networkd-lldp-tx.h"
+@@ -2056,10 +2055,6 @@ int link_configure(Link *link) {
+ if (r < 0)
+ return r;
+
+- r = link_set_ipv6_proxy_ndp_addresses(link);
+- if (r < 0)
+- return r;
+-
+ r = link_set_mac(link);
+ if (r < 0)
+ return r;
diff --git a/debian/patches/pkg-config-make-prefix-overridable-again.patch b/debian/patches/pkg-config-make-prefix-overridable-again.patch
new file mode 100644
index 0000000..68e50bc
--- /dev/null
+++ b/debian/patches/pkg-config-make-prefix-overridable-again.patch
@@ -0,0 +1,75 @@
+From: Jan Tojnar <jtojnar@gmail.com>
+Date: Sat, 2 Jan 2021 02:46:33 +0100
+Subject: pkg-config: make prefix overridable again
+
+While we don't support prefix being != /usr, and this is hardcoded
+all over the place, variables in pkg-config file are expected
+to have overridable base directory.
+
+This is important for at least the following two use cases:
+
+- Installing projects to non-FHS package-specific prefixes for Nix-style
+ package managers. Of course, it is then their responsibility
+ to ensure systemd can find the service files.
+- Installing to local path for development purposes.
+ This is a compromise between running a program from a build directory,
+ and running it fully installed to system prefix.
+
+You will not want to write to system prefix in either case.
+
+For more information, see also
+https://www.bassi.io/articles/2018/03/15/pkg-config-and-paths/
+
+Fixes https://github.com/systemd/systemd/issues/18082
+
+Partially reverts 6e65df89c348242dbd10036abc7dd5e8181cf733
+
+(cherry picked from commit 60bce7c6d9606185114df1bdcd5ea100407688b8)
+---
+ src/core/systemd.pc.in | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
+index f2c0455..b5cc8f9 100644
+--- a/src/core/systemd.pc.in
++++ b/src/core/systemd.pc.in
+@@ -26,10 +26,10 @@ systemdsystemunitdir=${systemd_system_unit_dir}
+ systemd_system_preset_dir=${rootprefix}/lib/systemd/system-preset
+ systemdsystempresetdir=${systemd_system_preset_dir}
+
+-systemd_user_unit_dir=/usr/lib/systemd/user
++systemd_user_unit_dir=${prefix}/lib/systemd/user
+ systemduserunitdir=${systemd_user_unit_dir}
+
+-systemd_user_preset_dir=/usr/lib/systemd/user-preset
++systemd_user_preset_dir=${prefix}/lib/systemd/user-preset
+ systemduserpresetdir=${systemd_user_preset_dir}
+
+ systemd_system_conf_dir=${sysconfdir}/systemd/system
+@@ -47,7 +47,7 @@ systemduserunitpath=${systemd_user_unit_path}
+ systemd_system_generator_dir=${root_prefix}/lib/systemd/system-generators
+ systemdsystemgeneratordir=${systemd_system_generator_dir}
+
+-systemd_user_generator_dir=/usr/lib/systemd/user-generators
++systemd_user_generator_dir=${prefix}/lib/systemd/user-generators
+ systemdusergeneratordir=${systemd_user_generator_dir}
+
+ systemd_system_generator_path=/run/systemd/system-generators:/etc/systemd/system-generators:/usr/local/lib/systemd/system-generators:${systemd_system_generator_dir}
+@@ -62,7 +62,7 @@ systemdsleepdir=${systemd_sleep_dir}
+ systemd_shutdown_dir=${root_prefix}/lib/systemd/system-shutdown
+ systemdshutdowndir=${systemd_shutdown_dir}
+
+-tmpfiles_dir=/usr/lib/tmpfiles.d
++tmpfiles_dir=${prefix}/lib/tmpfiles.d
+ tmpfilesdir=${tmpfiles_dir}
+
+ sysusers_dir=${rootprefix}/lib/sysusers.d
+@@ -77,7 +77,7 @@ binfmtdir=${binfmt_dir}
+ modules_load_dir=${rootprefix}/lib/modules-load.d
+ modulesloaddir=${modules_load_dir}
+
+-catalog_dir=/usr/lib/systemd/catalog
++catalog_dir=${prefix}/lib/systemd/catalog
+ catalogdir=${catalog_dir}
+
+ system_uid_max=@SYSTEM_UID_MAX@
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@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@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@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/rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch b/debian/patches/rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch
new file mode 100644
index 0000000..d338f49
--- /dev/null
+++ b/debian/patches/rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch
@@ -0,0 +1,41 @@
+From: Vincent Pelletier <plr.vincent@gmail.com>
+Date: Sat, 27 Feb 2021 00:17:06 +0000
+Subject: rules: Move ID_SMARTCARD_READER definition to a <70 configuration.
+
+70-uaccess.rules sets the uaccess tag on devices with ID_SMARTCARD_READER
+set, but it is set in 99-systemd.rules .
+Move this to a 60-*.rules which already matches USB CCID class, factorising
+the matching, so 70-uaccess.rules sets up these devices as expected.
+
+(cherry picked from commit dbdcd51f78bde5e9033d98d61bbb750c868bde9d)
+---
+ rules.d/60-fido-id.rules | 3 ++-
+ rules.d/99-systemd.rules.in | 1 -
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/rules.d/60-fido-id.rules b/rules.d/60-fido-id.rules
+index c7d5d2f..48c259e 100644
+--- a/rules.d/60-fido-id.rules
++++ b/rules.d/60-fido-id.rules
+@@ -7,7 +7,8 @@ SUBSYSTEM=="hidraw", IMPORT{program}="fido_id"
+ # Tag any form of security token as such
+ ENV{ID_SECURITY_TOKEN}=="1", TAG+="security-device"
+
++SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0b????:*", ENV{ID_SMARTCARD_READER}="1"
+ # Tag any CCID device (i.e. Smartcard Reader) as security token
+-SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="0b", TAG+="security-device"
++ENV{ID_SMARTCARD_READER}=="1", TAG+="security-device"
+
+ LABEL="fido_id_end"
+diff --git a/rules.d/99-systemd.rules.in b/rules.d/99-systemd.rules.in
+index 7c22eef..0abca6e 100644
+--- a/rules.d/99-systemd.rules.in
++++ b/rules.d/99-systemd.rules.in
+@@ -49,7 +49,6 @@ SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsys
+ SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k", \
+ ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target"
+
+-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0b????:*", ENV{ID_SMARTCARD_READER}="1"
+ ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target"
+ SUBSYSTEM=="sound", KERNEL=="controlC*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target"
+
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..f81d7b4
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,66 @@
+Add-helper-for-case-independent-string-equality-checks.patch
+localed-Run-locale-gen-if-available-to-generate-missing-l.patch
+core-fix-mtime-calculation-of-dropin-files.patch
+analyze-slightly-reword-PrivateTmp-message.patch
+rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch
+table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch
+pkg-config-make-prefix-overridable-again.patch
+LoadCredentials-do-not-assert-on-invalid-syntax.patch
+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
+Drop-bundled-copy-of-linux-if_arp.h.patch
+virt-Support-detection-for-ARM64-Hyper-V-guests.patch
+virt-Fix-the-detection-for-Hyper-V-VMs.patch
+virt-detect-OpenStack-Nova-instance.patch
+ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch
+udev-always-create-device-symlinks-for-USB-disks.patch
+Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch
+udev-first-set-properties-based-on-usb-subsystem.patch
+logind-fix-getting-property-OnExternalPower-via-D-Bus.patch
+coredump-do-not-allow-user-to-access-coredumps-with-chang.patch
+time-util-fix-buffer-over-run.patch
+machined-varlink-fix-double-free.patch
+Always-free-deserialized_subscribed-on-reload.patch
+shared-calendarspec-abort-calculation-after-1000-iteratio.patch
+shared-calendarspec-when-mktime-moves-us-backwards-jump-f.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
+debian/Add-support-for-TuxOnIce-hibernation.patch
+debian/Re-enable-journal-forwarding-to-syslog.patch
+debian/Don-t-enable-audit-by-default.patch
+debian/Only-start-logind-if-dbus-is-installed.patch
+debian/fsckd-daemon-for-inter-fsckd-communication.patch
+debian/Skip-filesystem-check-if-already-done-by-the-initram.patch
+debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch
+debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch
+debian/Let-graphical-session-pre.target-be-manually-started.patch
+debian/Add-env-variable-for-machine-ID-path.patch
+debian/Drop-seccomp-system-call-filter-for-udev.patch
+debian/deny-list-upstream-test-25.patch
+debian/deny-list-upstream-test-02-ppc64el.patch
+debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch
+debian/Keep-journal-files-compatible-with-older-versions.patch
+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-calendarspec-abort-calculation-after-1000-iteratio.patch b/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch
new file mode 100644
index 0000000..49562a7
--- /dev/null
+++ b/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch
@@ -0,0 +1,55 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Sun, 21 Mar 2021 20:59:32 +0100
+Subject: shared/calendarspec: abort calculation after 1000 iterations
+
+We have a bug where we seem to enter an infinite loop when running in the
+Europe/Dublin timezone. The timezone is "special" because it has negative SAVE
+values. The handling of this should obviously be fixed, but let's use a
+belt-and-suspenders approach, and gracefully fail if we fail to find an answer
+within a specific number of attempts. The code in this function is rather
+complex, and it's hard to rule out another bug in the future.
+
+(cherry picked from commit 169615c9a8cdc54d748d4dfc8279be9b3c2bec44)
+---
+ src/shared/calendarspec.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c
+index 7162592..80acc57 100644
+--- a/src/shared/calendarspec.c
++++ b/src/shared/calendarspec.c
+@@ -1211,6 +1211,10 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
+ return (weekdays_bits & (1 << k));
+ }
+
++/* A safety valve: if we get stuck in the calculation, return an error.
++ * C.f. https://bugzilla.redhat.com/show_bug.cgi?id=1941335. */
++#define MAX_CALENDAR_ITERATIONS 1000
++
+ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
+ struct tm c;
+ int tm_usec;
+@@ -1224,7 +1228,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
+ c = *tm;
+ tm_usec = *usec;
+
+- for (;;) {
++ for (unsigned iteration = 0; iteration < MAX_CALENDAR_ITERATIONS; iteration++) {
+ /* Normalize the current date */
+ (void) mktime_or_timegm(&c, spec->utc);
+ c.tm_isdst = spec->dst;
+@@ -1321,6 +1325,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
+ *usec = tm_usec;
+ return 0;
+ }
++
++ /* It seems we entered an infinite loop. Let's gracefully return an error instead of hanging or
++ * aborting. This code is also exercised when timers.target is brought up during early boot, so
++ * aborting here is problematic and hard to diagnose for users. */
++ _cleanup_free_ char *s = NULL;
++ (void) calendar_spec_to_string(spec, &s);
++ return log_warning_errno(SYNTHETIC_ERRNO(EDEADLK),
++ "Infinite loop in calendar calculation: %s", strna(s));
+ }
+
+ static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) {
diff --git a/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch b/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch
new file mode 100644
index 0000000..9503c1e
--- /dev/null
+++ b/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch
@@ -0,0 +1,105 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Mon, 22 Mar 2021 12:51:47 +0100
+Subject: shared/calendarspec: when mktime() moves us backwards, jump forward
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+When trying to calculate the next firing of 'Sun *-*-* 01:00:00', we'd fall
+into an infinite loop, because mktime() moves us "backwards":
+
+Before this patch:
+tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00
+tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00
+tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00
+...
+
+We rely on mktime() normalizing the time. The man page does not say that it'll
+move the time forward, but our algorithm relies on this. So let's catch this
+case explicitly.
+
+With this patch:
+$ TZ=Europe/Dublin faketime 2021-03-21 build/systemd-analyze calendar --iterations=5 'Sun *-*-* 01:00:00'
+Normalized form: Sun *-*-* 01:00:00
+ Next elapse: Sun 2021-03-21 01:00:00 GMT
+ (in UTC): Sun 2021-03-21 01:00:00 UTC
+ From now: 59min left
+ Iter. #2: Sun 2021-04-04 01:00:00 IST
+ (in UTC): Sun 2021-04-04 00:00:00 UTC
+ From now: 1 weeks 6 days left <---- note the 2 week jump here
+ Iter. #3: Sun 2021-04-11 01:00:00 IST
+ (in UTC): Sun 2021-04-11 00:00:00 UTC
+ From now: 2 weeks 6 days left
+ Iter. #4: Sun 2021-04-18 01:00:00 IST
+ (in UTC): Sun 2021-04-18 00:00:00 UTC
+ From now: 3 weeks 6 days left
+ Iter. #5: Sun 2021-04-25 01:00:00 IST
+ (in UTC): Sun 2021-04-25 00:00:00 UTC
+ From now: 1 months 4 days left
+
+Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1941335.
+
+(cherry picked from commit 129cb6e249bef30dc33e08f98f0b27a6de976f6f)
+---
+ src/shared/calendarspec.c | 19 +++++++++++--------
+ src/test/test-calendarspec.c | 3 +++
+ test/test-functions | 1 +
+ 3 files changed, 15 insertions(+), 8 deletions(-)
+
+diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c
+index 80acc57..c8d97c3 100644
+--- a/src/shared/calendarspec.c
++++ b/src/shared/calendarspec.c
+@@ -1185,15 +1185,18 @@ static int tm_within_bounds(struct tm *tm, bool utc) {
+ return negative_errno();
+
+ /* Did any normalization take place? If so, it was out of bounds before */
+- bool good = t.tm_year == tm->tm_year &&
+- t.tm_mon == tm->tm_mon &&
+- t.tm_mday == tm->tm_mday &&
+- t.tm_hour == tm->tm_hour &&
+- t.tm_min == tm->tm_min &&
+- t.tm_sec == tm->tm_sec;
+- if (!good)
++ int cmp = CMP(t.tm_year, tm->tm_year) ?:
++ CMP(t.tm_mon, tm->tm_mon) ?:
++ CMP(t.tm_mday, tm->tm_mday) ?:
++ CMP(t.tm_hour, tm->tm_hour) ?:
++ CMP(t.tm_min, tm->tm_min) ?:
++ CMP(t.tm_sec, tm->tm_sec);
++
++ if (cmp < 0)
++ return -EDEADLK; /* Refuse to go backward */
++ if (cmp > 0)
+ *tm = t;
+- return good;
++ return cmp == 0;
+ }
+
+ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
+diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
+index e0b7f22..1b04186 100644
+--- a/src/test/test-calendarspec.c
++++ b/src/test/test-calendarspec.c
+@@ -218,6 +218,9 @@ int main(int argc, char* argv[]) {
+ // Confirm that timezones in the Spec work regardless of current timezone
+ test_next("2017-09-09 20:42:00 Pacific/Auckland", "", 12345, 1504946520000000);
+ test_next("2017-09-09 20:42:00 Pacific/Auckland", "EET", 12345, 1504946520000000);
++ /* Check that we don't start looping if mktime() moves us backwards */
++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "", 1616412478000000, 1617494400000000);
++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "IST", 1616412478000000, 1617494400000000);
+
+ assert_se(calendar_spec_from_string("test", &c) < 0);
+ assert_se(calendar_spec_from_string(" utc", &c) < 0);
+diff --git a/test/test-functions b/test/test-functions
+index 52b52bf..beaf4fa 100644
+--- a/test/test-functions
++++ b/test/test-functions
+@@ -1120,6 +1120,7 @@ install_zoneinfo() {
+ inst_any /usr/share/zoneinfo/Asia/Vladivostok
+ inst_any /usr/share/zoneinfo/Australia/Sydney
+ inst_any /usr/share/zoneinfo/Europe/Berlin
++ inst_any /usr/share/zoneinfo/Europe/Dublin
+ inst_any /usr/share/zoneinfo/Europe/Kiev
+ inst_any /usr/share/zoneinfo/Pacific/Auckland
+ inst_any /usr/share/zoneinfo/Pacific/Honolulu
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 0000000..f870a3e
--- /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 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@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@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@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/table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch b/debian/patches/table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch
new file mode 100644
index 0000000..fea1aec
--- /dev/null
+++ b/debian/patches/table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch
@@ -0,0 +1,167 @@
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Tue, 2 Feb 2021 01:47:58 +0900
+Subject: table: drop trailing white spaces of the last cell in row
+
+Fixes #18415.
+
+(cherry picked from commit 71894e18313e41a72cecdc77fea5037f95d6903f)
+---
+ src/shared/format-table.c | 6 ++++++
+ src/test/test-format-table.c | 40 ++++++++++++++++++++--------------------
+ 2 files changed, 26 insertions(+), 20 deletions(-)
+
+diff --git a/src/shared/format-table.c b/src/shared/format-table.c
+index a13a198..2dc95e9 100644
+--- a/src/shared/format-table.c
++++ b/src/shared/format-table.c
+@@ -2155,6 +2155,12 @@ int table_print(Table *t, FILE *f) {
+ if (!aligned)
+ return -ENOMEM;
+
++ /* Drop trailing white spaces of last column when no cosmetics is set. */
++ if (j == display_columns - 1 &&
++ (!colors_enabled() || !table_data_color(d)) &&
++ (!urlify_enabled() || !d->url))
++ delete_trailing_chars(aligned, NULL);
++
+ free_and_replace(buffer, aligned);
+ field = buffer;
+ }
+diff --git a/src/test/test-format-table.c b/src/test/test-format-table.c
+index 24ee1df..b2943e6 100644
+--- a/src/test/test-format-table.c
++++ b/src/test/test-format-table.c
+@@ -29,7 +29,7 @@ static void test_issue_9549(void) {
+
+ printf("%s\n", formatted);
+ assert_se(streq(formatted,
+- "NAME TYPE RO USAGE CREATED MODIFIED \n"
++ "NAME TYPE RO USAGE CREATED MODIFIED\n"
+ "foooo raw no 673.6M Wed 2018-07-11 00:10:33 J… Wed 2018-07-11 00:16:00 JST\n"
+ ));
+ }
+@@ -72,7 +72,7 @@ static void test_multiline(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"));
++ "lines \n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, (size_t) -1);
+@@ -82,7 +82,7 @@ static void test_multiline(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"));
++ "lines \n"));
+ formatted = mfree(formatted);
+
+ assert_se(table_add_many(table,
+@@ -123,7 +123,7 @@ static void test_multiline(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"
++ "lines \n"
+ "short a\n"
+ " pair\n"
+ "short2 a\n"
+@@ -138,7 +138,7 @@ static void test_multiline(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"
++ "lines \n"
+ "short a\n"
+ " pair\n"
+ "short2 a\n"
+@@ -186,7 +186,7 @@ static void test_strv(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"));
++ "lines \n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, (size_t) -1);
+@@ -196,7 +196,7 @@ static void test_strv(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"));
++ "lines \n"));
+ formatted = mfree(formatted);
+
+ assert_se(table_add_many(table,
+@@ -237,7 +237,7 @@ static void test_strv(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"
++ "lines \n"
+ "short a\n"
+ " pair\n"
+ "short2 a\n"
+@@ -252,7 +252,7 @@ static void test_strv(void) {
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+- "lines \n"
++ "lines \n"
+ "short a\n"
+ " pair\n"
+ "short2 a\n"
+@@ -333,7 +333,7 @@ static void test_strv_wrapped(void) {
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three different two lines\n"
+- "lines \n"
++ "lines \n"
+ "short a pair\n"
+ "short2 a eight line ćęłł\n"
+ " ___5___ ___6___…\n"));
+@@ -345,7 +345,7 @@ static void test_strv_wrapped(void) {
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three different two lines\n"
+- "lines \n"
++ "lines \n"
+ "short a pair\n"
+ "short2 a eight line ćęłł\n"
+ " ___5___ ___6___\n"
+@@ -358,7 +358,7 @@ static void test_strv_wrapped(void) {
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three different two lines\n"
+- "lines \n"
++ "lines \n"
+ "short a pair\n"
+ "short2 a eight line ćęłł\n"
+ " ___5___ ___6___\n"
+@@ -493,17 +493,17 @@ int main(int argc, char *argv[]) {
+ if (isatty(STDOUT_FILENO))
+ assert_se(streq(formatted,
+ " no a long f… no a long f… a long fi…\n"
+- " no fäää no fäää fäää \n"
+- " yes fäää yes fäää fäää \n"
+- " yes xxx yes xxx xxx \n"
+- "5min 5min \n"));
++ " no fäää no fäää fäää\n"
++ " yes fäää yes fäää fäää\n"
++ " yes xxx yes xxx xxx\n"
++ "5min 5min \n"));
+ else
+ assert_se(streq(formatted,
+ " no a long field no a long field a long field\n"
+- " no fäää no fäää fäää \n"
+- " yes fäää yes fäää fäää \n"
+- " yes xxx yes xxx xxx \n"
+- "5min 5min \n"));
++ " no fäää no fäää fäää\n"
++ " yes fäää yes fäää fäää\n"
++ " yes xxx yes xxx xxx\n"
++ "5min 5min \n"));
+
+ test_issue_9549();
+ test_multiline();
diff --git a/debian/patches/time-util-fix-buffer-over-run.patch b/debian/patches/time-util-fix-buffer-over-run.patch
new file mode 100644
index 0000000..65a6ed0
--- /dev/null
+++ b/debian/patches/time-util-fix-buffer-over-run.patch
@@ -0,0 +1,55 @@
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Thu, 7 Jul 2022 18:27:02 +0900
+Subject: time-util: fix buffer-over-run
+
+Fixes #23928.
+
+(cherry picked from commit 9102c625a673a3246d7e73d8737f3494446bad4e)
+(cherry picked from commit 72d4c15a946d20143cd4c6783c802124bc894dc7)
+(cherry picked from commit c32530f5bdd11c74e8f5a86eecd7c36b3bae739f)
+(cherry picked from commit b2a25b5e64345bd0bb7697a956d33afd6980286a)
+(cherry picked from commit 858dc1ad609290cc4ca288acf87046ee295c3d51)
+---
+ src/basic/time-util.c | 2 +-
+ src/test/test-time-util.c | 8 ++++++++
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/src/basic/time-util.c b/src/basic/time-util.c
+index 5318d63..1909710 100644
+--- a/src/basic/time-util.c
++++ b/src/basic/time-util.c
+@@ -574,7 +574,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
+ t = b;
+ }
+
+- n = MIN((size_t) k, l);
++ n = MIN((size_t) k, l-1);
+
+ l -= n;
+ p += n;
+diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c
+index cc391e8..877b24c 100644
+--- a/src/test/test-time-util.c
++++ b/src/test/test-time-util.c
+@@ -243,6 +243,13 @@ static void test_format_timespan(usec_t accuracy) {
+ test_format_timespan_one(USEC_INFINITY, accuracy);
+ }
+
++static void test_format_timespan2(void) {
++ /* See issue #23928. */
++ _cleanup_free_ char *buf;
++ assert_se(buf = new(char, 5));
++ assert_se(buf == format_timespan(buf, 5, 100005, 1000));
++}
++
+ static void test_timezone_is_valid(void) {
+ log_info("/* %s */", __func__);
+
+@@ -533,6 +540,7 @@ int main(int argc, char *argv[]) {
+ test_format_timespan(1);
+ test_format_timespan(USEC_PER_MSEC);
+ test_format_timespan(USEC_PER_SEC);
++ test_format_timespan2();
+ test_timezone_is_valid();
+ test_get_timezones();
+ test_usec_add();
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@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/udev-always-create-device-symlinks-for-USB-disks.patch b/debian/patches/udev-always-create-device-symlinks-for-USB-disks.patch
new file mode 100644
index 0000000..f373b68
--- /dev/null
+++ b/debian/patches/udev-always-create-device-symlinks-for-USB-disks.patch
@@ -0,0 +1,111 @@
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Sun, 30 Oct 2022 09:43:05 +0900
+Subject: udev: always create device symlinks for USB disks
+
+Previously, ata_id might not be able to retrieve attributes correctly,
+and properties from usb_id were used as a fallback. See issue #24921
+and PR #24923. To keep backward compatibility, still we need to create
+symlinks based on USB serial.
+
+Fixes #25179.
+---
+ rules.d/60-persistent-storage.rules | 10 +++++--
+ src/udev/udev-builtin-usb_id.c | 55 +++++++++++++++++++++++++++++--------
+ 2 files changed, 51 insertions(+), 14 deletions(-)
+
+diff --git a/rules.d/60-persistent-storage.rules b/rules.d/60-persistent-storage.rules
+index fc7f733..99e0c9a 100644
+--- a/rules.d/60-persistent-storage.rules
++++ b/rules.d/60-persistent-storage.rules
+@@ -59,14 +59,20 @@ KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}==
+ # Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures)
+ KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode"
+
+-# Fall back usb_id for USB devices
+-KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
++# Also import properties from usb_id for USB devices
++KERNEL=="sd*[!0-9]|sr*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+
+ # SCSI devices
+ KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi"
+ KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss"
+ KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+ KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
++# Previously, ata_id in the above might not be able to retrieve attributes correctly,
++# and properties from usb_id were used as a fallback. See issue #24921 and PR #24923.
++# To keep backward compatibility, still we need to create symlinks based on USB serial.
++# See issue #25179.
++KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_USB_SERIAL}=="?*", SYMLINK+="disk/by-id/usb-$env{ID_USB_SERIAL}"
++KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_USB_SERIAL}=="?*", SYMLINK+="disk/by-id/usb-$env{ID_USB_SERIAL}-part%n"
+
+ # PMEM devices
+ KERNEL=="pmem*", ENV{DEVTYPE}=="disk", ATTRS{uuid}=="?*", SYMLINK+="disk/by-id/pmem-$attr{uuid}"
+diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c
+index fa554e7..3910ffa 100644
+--- a/src/udev/udev-builtin-usb_id.c
++++ b/src/udev/udev-builtin-usb_id.c
+@@ -430,21 +430,52 @@ fallback:
+ if (!isempty(instance_str))
+ strpcpyl(&s, l, "-", instance_str, NULL);
+
+- udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
+- udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc);
+- udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id);
+- udev_builtin_add_property(dev, test, "ID_MODEL", model_str);
+- udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc);
+- udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id);
+- udev_builtin_add_property(dev, test, "ID_REVISION", revision_str);
+- udev_builtin_add_property(dev, test, "ID_SERIAL", serial);
++ if (sd_device_get_property_value(dev, "ID_BUS", NULL) >= 0)
++ log_device_debug(dev, "ID_BUS property is already set, setting only properties prefixed with \"ID_USB_\".");
++ else {
++ udev_builtin_add_property(dev, test, "ID_BUS", "usb");
++
++ udev_builtin_add_property(dev, test, "ID_MODEL", model_str);
++ udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc);
++ udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id);
++
++ udev_builtin_add_property(dev, test, "ID_SERIAL", serial);
++ if (!isempty(serial_str))
++ udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str);
++
++ udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
++ udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc);
++ udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id);
++
++ udev_builtin_add_property(dev, test, "ID_REVISION", revision_str);
++
++ if (!isempty(type_str))
++ udev_builtin_add_property(dev, test, "ID_TYPE", type_str);
++
++ if (!isempty(instance_str))
++ udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str);
++ }
++
++ /* Also export the same values in the above by prefixing ID_USB_. */
++ udev_builtin_add_property(dev, test, "ID_USB_MODEL", model_str);
++ udev_builtin_add_property(dev, test, "ID_USB_MODEL_ENC", model_str_enc);
++ udev_builtin_add_property(dev, test, "ID_USB_MODEL_ID", product_id);
++ udev_builtin_add_property(dev, test, "ID_USB_SERIAL", serial);
+ if (!isempty(serial_str))
+- udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str);
++ udev_builtin_add_property(dev, test, "ID_USB_SERIAL_SHORT", serial_str);
++
++ udev_builtin_add_property(dev, test, "ID_USB_VENDOR", vendor_str);
++ udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ENC", vendor_str_enc);
++ udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ID", vendor_id);
++
++ udev_builtin_add_property(dev, test, "ID_USB_REVISION", revision_str);
++
+ if (!isempty(type_str))
+- udev_builtin_add_property(dev, test, "ID_TYPE", type_str);
++ udev_builtin_add_property(dev, test, "ID_USB_TYPE", type_str);
++
+ if (!isempty(instance_str))
+- udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str);
+- udev_builtin_add_property(dev, test, "ID_BUS", "usb");
++ udev_builtin_add_property(dev, test, "ID_USB_INSTANCE", instance_str);
++
+ if (!isempty(packed_if_str))
+ udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str);
+ if (ifnum)
diff --git a/debian/patches/udev-first-set-properties-based-on-usb-subsystem.patch b/debian/patches/udev-first-set-properties-based-on-usb-subsystem.patch
new file mode 100644
index 0000000..0aee515
--- /dev/null
+++ b/debian/patches/udev-first-set-properties-based-on-usb-subsystem.patch
@@ -0,0 +1,34 @@
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Thu, 3 Nov 2022 09:39:36 +0900
+Subject: udev: first set properties based on usb subsystem
+
+After 479da1107a0d4e2f7ef5cd938512b87a0e45f180, the usb_id builtin
+command does not set ID_SERIAL if ID_BUS is already set.
+Before the commit, all properties set based on pci bus were overwritten
+by the usb_id, hence now it is sufficient setting them only when ID_BUS is
+not set yet.
+
+Fixes #25238.
+
+(cherry picked from commit 01e704eba982fbc1517287cd261d229ff8e0a779)
+---
+ rules.d/60-serial.rules | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/rules.d/60-serial.rules b/rules.d/60-serial.rules
+index f303e27..c133f26 100644
+--- a/rules.d/60-serial.rules
++++ b/rules.d/60-serial.rules
+@@ -3,9 +3,10 @@
+ ACTION=="remove", GOTO="serial_end"
+ SUBSYSTEM!="tty", GOTO="serial_end"
+
+-SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+-SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
+ SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
++SUBSYSTEMS=="pci", ENV{ID_BUS}=="", ENV{ID_BUS}="pci", \
++ ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}", \
++ IMPORT{builtin}="hwdb --subsystem=pci"
+
+ # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices
+ KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end"
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@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
new file mode 100644
index 0000000..c28266b
--- /dev/null
+++ b/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch
@@ -0,0 +1,59 @@
+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
+
+(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, const char *suffix, char **ret) {
+ if (!s)
+ return -ENOMEM;
+
++ if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */
++ return -ENAMETOOLONG;
++
+ /* 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 char *prefix, const char *path, const cha
+ if (!s)
+ return -ENOMEM;
+
++ if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */
++ return -ENAMETOOLONG;
++
+ /* 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(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);
+- test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -EINVAL);
++ test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -ENAMETOOLONG);
+ }
+
+ 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_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);
+- test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -EINVAL);
++ test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -ENAMETOOLONG);
+ }
+
+ static void test_unit_name_to_path_one(const char *unit, const char *path, int ret) {
diff --git a/debian/patches/virt-Fix-the-detection-for-Hyper-V-VMs.patch b/debian/patches/virt-Fix-the-detection-for-Hyper-V-VMs.patch
new file mode 100644
index 0000000..9757987
--- /dev/null
+++ b/debian/patches/virt-Fix-the-detection-for-Hyper-V-VMs.patch
@@ -0,0 +1,38 @@
+From: Boqun Feng <boqun.feng@gmail.com>
+Date: Tue, 23 Nov 2021 15:09:26 +0800
+Subject: virt: Fix the detection for Hyper-V VMs
+
+Use product_version instead of product_name in DMI table and the string
+"Hyper-V" to avoid misdetection.
+
+Fixes: #21468
+
+Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
+(cherry picked from commit 76eec0649936d9ae2f9087769f463feaf0cf5cb4)
+---
+ src/basic/virt.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/basic/virt.c b/src/basic/virt.c
+index 0d45ee6..54befd9 100644
+--- a/src/basic/virt.c
++++ b/src/basic/virt.c
+@@ -140,7 +140,8 @@ static int detect_vm_dmi(void) {
+ "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
+ "/sys/class/dmi/id/sys_vendor",
+ "/sys/class/dmi/id/board_vendor",
+- "/sys/class/dmi/id/bios_vendor"
++ "/sys/class/dmi/id/bios_vendor",
++ "/sys/class/dmi/id/product_version" /* For Hyper-V VMs test */
+ };
+
+ static const struct {
+@@ -158,7 +159,7 @@ static int detect_vm_dmi(void) {
+ { "Parallels", VIRTUALIZATION_PARALLELS },
+ /* https://wiki.freebsd.org/bhyve */
+ { "BHYVE", VIRTUALIZATION_BHYVE },
+- { "Microsoft", VIRTUALIZATION_MICROSOFT },
++ { "Hyper-V", VIRTUALIZATION_MICROSOFT },
+ };
+ unsigned i;
+ int r;
diff --git a/debian/patches/virt-Support-detection-for-ARM64-Hyper-V-guests.patch b/debian/patches/virt-Support-detection-for-ARM64-Hyper-V-guests.patch
new file mode 100644
index 0000000..373a77f
--- /dev/null
+++ b/debian/patches/virt-Support-detection-for-ARM64-Hyper-V-guests.patch
@@ -0,0 +1,28 @@
+From: Boqun Feng <boqun.feng@gmail.com>
+Date: Wed, 13 Oct 2021 11:32:09 +0800
+Subject: virt: Support detection for ARM64 Hyper-V guests
+
+The detection of Microsoft Hyper-V VMs is done by cpuid currently,
+however there is no cpuid on ARM64. And since ARM64 is now a supported
+architecture for Microsoft Hyper-V guests[1], then use DMI tables to
+detect a Hyper-V guest, which is more generic and works for ARM64.
+
+[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7aff79e297ee1aa0126924921fd87a4ae59d2467
+
+(cherry picked from commit 506bbc8569014253ea8614b680ccbc4fc2513a87)
+---
+ src/basic/virt.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/basic/virt.c b/src/basic/virt.c
+index 7d78a40..0d45ee6 100644
+--- a/src/basic/virt.c
++++ b/src/basic/virt.c
+@@ -158,6 +158,7 @@ static int detect_vm_dmi(void) {
+ { "Parallels", VIRTUALIZATION_PARALLELS },
+ /* https://wiki.freebsd.org/bhyve */
+ { "BHYVE", VIRTUALIZATION_BHYVE },
++ { "Microsoft", VIRTUALIZATION_MICROSOFT },
+ };
+ unsigned i;
+ int r;
diff --git a/debian/patches/virt-detect-OpenStack-Nova-instance.patch b/debian/patches/virt-detect-OpenStack-Nova-instance.patch
new file mode 100644
index 0000000..87686b2
--- /dev/null
+++ b/debian/patches/virt-detect-OpenStack-Nova-instance.patch
@@ -0,0 +1,21 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Sun, 7 Aug 2022 15:21:12 +0200
+Subject: virt: detect OpenStack Nova instance
+
+(cherry picked from commit 01d9fbccddd694bc584aed24eaa0543f831dc929)
+---
+ src/basic/virt.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/basic/virt.c b/src/basic/virt.c
+index 54befd9..78ee1b8 100644
+--- a/src/basic/virt.c
++++ b/src/basic/virt.c
+@@ -149,6 +149,7 @@ static int detect_vm_dmi(void) {
+ int id;
+ } dmi_vendor_table[] = {
+ { "KVM", VIRTUALIZATION_KVM },
++ { "OpenStack", VIRTUALIZATION_KVM }, /* Detect OpenStack instance as KVM in non x86 architecture */
+ { "QEMU", VIRTUALIZATION_QEMU },
+ { "VMware", VIRTUALIZATION_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */
+ { "VMW", VIRTUALIZATION_VMWARE },