diff -pruN 2.3.4-1/debian/changelog 2.3.4-1ubuntu2/debian/changelog
--- 2.3.4-1/debian/changelog	2025-08-26 16:38:22.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/changelog	2025-09-04 19:31:49.000000000 +0000
@@ -1,3 +1,46 @@
+zfs-linux (2.3.4-1ubuntu2) questing; urgency=medium
+
+  * dracut: Open and mount luks keystore (LP: #2070066)
+
+ -- Benjamin Drung <bdrung@ubuntu.com>  Thu, 04 Sep 2025 21:31:49 +0200
+
+zfs-linux (2.3.4-1ubuntu1) questing; urgency=medium
+
+  * Merge from Debian (LP: #2121500)
+    - debian/control:
+      - drop dependencies on "zfs-modules | zfs-dkms" such that all
+        packages can be installed in containers, on hosts that have zfs module
+        loaded
+    - debian/patches:
+      - adding
+        - ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch
+        - ubuntu/4000-zsys-support.patch
+        - ubuntu/0001-Revert-Revert-systemd-Use-non-absolute-paths-in-
+          Exec.patch
+        - ubuntu/4100-disable-bpool-upgrade.patch
+        - ubuntu/zfs-mount-container-start.patch
+        - ubuntu/fixup-abi.patch
+        - ubuntu/4510-silently-ignore-modprobe-failure.patch
+        - ubuntu/4751-suppress-types.patch
+        - ubuntu/0003-enable-linux-experimental.patch
+        - ubuntu/0004-Linux-6.17-add-config-kernel-dentry-operations-m4.patch
+        - ubuntu/0005-Linux-6.17-compat-config-restore-ZFS_AC_KERNEL_DENTRY-tests.patch
+        - ubuntu/0006-Linux-6.17-compat-d_set_d_op-is-no-longer-available.patch
+      - removing
+        - ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-serialise-han.patch
+        - ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch
+    - debian/rules:
+      - enforce abi check on Ubuntu amd64
+      - allow kernel version not matching META
+    - debian/tests/:
+      - reduce testing to smoketest only
+      - Ubuntu Kernel regression testing covers zfs testsuite
+      - Drop depends on linux-headers-*.
+      - draid tests fail upstream, so marking failures as expected
+        to fail
+
+ -- John Cabaj <john.cabaj@canonical.com>  Tue, 26 Aug 2025 13:48:56 -0500
+
 zfs-linux (2.3.4-1) unstable; urgency=medium
 
   * New upstream version 2.3.4 (closes: #1111252).
diff -pruN 2.3.4-1/debian/control 2.3.4-1ubuntu2/debian/control
--- 2.3.4-1/debian/control	2025-03-18 12:24:04.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/control	2025-09-04 08:22:32.000000000 +0000
@@ -1,7 +1,8 @@
 Source: zfs-linux
 Section: contrib/kernel
 Priority: optional
-Maintainer: Debian ZFS on Linux maintainers <pkg-zfsonlinux-devel@alioth-lists.debian.net>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Debian ZFS on Linux maintainers <pkg-zfsonlinux-devel@alioth-lists.debian.net
 Uploaders: Aron Xu <aron@debian.org>,
            Carlos Alberto Lopez Perez <clopez@igalia.com>,
            Mo Zhou <lumin@debian.org>,
@@ -216,7 +217,6 @@ Architecture: all
 Multi-Arch: foreign
 Depends: busybox-initramfs | busybox-static | busybox,
          initramfs-tools,
-         zfs-modules | zfs-dkms,
          zfsutils-linux (>= ${source:Version}),
          ${misc:Depends}
 Breaks: zfsutils-linux (<= 0.7.11-2)
@@ -233,7 +233,6 @@ Package: zfs-dracut
 Architecture: all
 Multi-Arch: foreign
 Depends: dracut,
-         zfs-modules | zfs-dkms,
          zfsutils-linux (>= ${source:Version}),
          ${misc:Depends}
 Description: OpenZFS root filesystem capabilities for Linux - dracut
@@ -255,7 +254,7 @@ Depends: libnvpair3linux (= ${binary:Ver
          python3,
          ${misc:Depends},
          ${shlibs:Depends}
-Recommends: zfs-modules | zfs-dkms, zfs-zed
+Recommends: zfs-zed
 Breaks: spl (<< 0.7.9-2),
         spl-dkms (<< 0.8.0~rc1),
         zfs-dkms (<< ${source:Version}),
@@ -278,8 +277,7 @@ Package: zfs-zed
 Section: contrib/admin
 Architecture: linux-any
 Pre-Depends: ${misc:Pre-Depends}
-Depends: zfs-modules | zfs-dkms,
-         zfsutils-linux (>= ${binary:Version}),
+Depends: zfsutils-linux (>= ${binary:Version}),
          ${misc:Depends},
          ${shlibs:Depends}
 Description: OpenZFS Event Daemon
@@ -309,7 +307,6 @@ Depends: acl,
          python3-pyzfs,
          sudo,
          sysstat,
-         zfs-modules | zfs-dkms,
          zfsutils-linux (>=${binary:Version}),
          ${misc:Depends},
          ${shlibs:Depends}
diff -pruN 2.3.4-1/debian/patches/series 2.3.4-1ubuntu2/debian/patches/series
--- 2.3.4-1/debian/patches/series	2025-08-23 06:05:13.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/series	2025-09-04 19:31:05.000000000 +0000
@@ -10,14 +10,19 @@ force-verbose-rules.patch
 move-arcstat-1-to-8.patch
 skip-on-PREEMPT_RT.patch
 cross-compile.patch
-#ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-serialise-han.patch
-#ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch
-#ubuntu/0003-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch
-#ubuntu/4000-zsys-support.patch
-#ubuntu/4100-disable-bpool-upgrade.patch
+ubuntu/0001-Revert-Revert-systemd-Use-non-absolute-paths-in-Exec.patch
+ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch
+ubuntu/4000-zsys-support.patch
+ubuntu/4001-dracut-Open-and-mount-luks-keystore.patch
+ubuntu/4100-disable-bpool-upgrade.patch
 ubuntu/zfs-mount-container-start.patch
-#ubuntu/4510-silently-ignore-modprobe-failure.patch
-#ubuntu/4751-suppress-types.patch
+ubuntu/4510-silently-ignore-modprobe-failure.patch
+ubuntu/4751-suppress-types.patch
+ubuntu/fixup-abi.patch
 fix-pyzfs-version.patch
 BUILD_EXCLUSIVE_KERNEL_MIN.patch
 #bump-Linux-Minimum.patch
+ubuntu/0003-enable-linux-experimental.patch
+ubuntu/0004-Linux-6.17-add-config-kernel-dentry-operations-m4.patch
+ubuntu/0005-Linux-6.17-compat-config-restore-ZFS_AC_KERNEL_DENTRY-tests.patch
+ubuntu/0006-Linux-6.17-compat-d_set_d_op-is-no-longer-available.patch
diff -pruN 2.3.4-1/debian/patches/ubuntu/0001-Revert-Revert-systemd-Use-non-absolute-paths-in-Exec.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0001-Revert-Revert-systemd-Use-non-absolute-paths-in-Exec.patch
--- 2.3.4-1/debian/patches/ubuntu/0001-Revert-Revert-systemd-Use-non-absolute-paths-in-Exec.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0001-Revert-Revert-systemd-Use-non-absolute-paths-in-Exec.patch	2025-09-04 19:31:05.000000000 +0000
@@ -0,0 +1,105 @@
+From bf69bed17f2b711a658c58e70c6c135a9fac4dea Mon Sep 17 00:00:00 2001
+From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+Date: Thu, 6 Jul 2023 18:08:38 +0100
+Subject: [PATCH] Revert "Revert "systemd: Use non-absolute paths in Exec*
+ lines""
+
+This reverts commit 6c962690245a6a2a4dfc2350c71a249641139c26.
+---
+ etc/systemd/system/zfs-import-cache.service.in |  2 +-
+ etc/systemd/system/zfs-import-scan.service.in  |  2 +-
+ etc/systemd/system/zfs-mount.service.in        |  2 +-
+ etc/systemd/system/zfs-scrub@.service.in       | 10 +++++-----
+ etc/systemd/system/zfs-share.service.in        |  2 +-
+ etc/systemd/system/zfs-trim@.service.in        | 10 +++++-----
+ etc/systemd/system/zfs-volume-wait.service.in  |  2 +-
+ etc/systemd/system/zfs-zed.service.in          |  2 +-
+ 8 files changed, 16 insertions(+), 16 deletions(-)
+
+--- a/etc/systemd/system/zfs-import-cache.service.in
++++ b/etc/systemd/system/zfs-import-cache.service.in
+@@ -17,7 +17,7 @@
+ Type=oneshot
+ RemainAfterExit=yes
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=@sbindir@/zpool import -c @sysconfdir@/zfs/zpool.cache -aN $ZPOOL_IMPORT_OPTS
++ExecStart=zpool import -c @sysconfdir@/zfs/zpool.cache -aN $ZPOOL_IMPORT_OPTS
+ 
+ [Install]
+ WantedBy=zfs-import.target
+--- a/etc/systemd/system/zfs-import-scan.service.in
++++ b/etc/systemd/system/zfs-import-scan.service.in
+@@ -16,7 +16,7 @@
+ Type=oneshot
+ RemainAfterExit=yes
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=@sbindir@/zpool import -aN -o cachefile=none $ZPOOL_IMPORT_OPTS
++ExecStart=zpool import -aN -o cachefile=none $ZPOOL_IMPORT_OPTS
+ 
+ [Install]
+ WantedBy=zfs-import.target
+--- a/etc/systemd/system/zfs-mount.service.in
++++ b/etc/systemd/system/zfs-mount.service.in
+@@ -19,7 +19,7 @@
+ Type=oneshot
+ RemainAfterExit=yes
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=@sbindir@/zfs mount -a
++ExecStart=zfs mount -a
+ 
+ [Install]
+ WantedBy=zfs.target
+--- a/etc/systemd/system/zfs-scrub@.service.in
++++ b/etc/systemd/system/zfs-scrub@.service.in
+@@ -8,8 +8,8 @@
+ 
+ [Service]
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=/bin/sh -c '\
+-if @sbindir@/zpool status %i | grep -q "scrub in progress"; then\
+-exec @sbindir@/zpool wait -t scrub %i;\
+-else exec @sbindir@/zpool scrub -w %i; fi'
+-ExecStop=-/bin/sh -c '@sbindir@/zpool scrub -p %i 2>/dev/null || true'
++ExecStart=sh -c '\
++if zpool status %i | grep -q "scrub in progress"; then\
++exec zpool wait -t scrub %i;\
++else exec zpool scrub -w %i; fi'
++ExecStop=-sh -c 'zpool scrub -p %i 2>/dev/null || true'
+--- a/etc/systemd/system/zfs-share.service.in
++++ b/etc/systemd/system/zfs-share.service.in
+@@ -14,7 +14,7 @@
+ Type=oneshot
+ RemainAfterExit=yes
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=@sbindir@/zfs share -a
++ExecStart=zfs share -a
+ 
+ [Install]
+ WantedBy=zfs.target
+--- a/etc/systemd/system/zfs-trim@.service.in
++++ b/etc/systemd/system/zfs-trim@.service.in
+@@ -8,8 +8,8 @@
+ 
+ [Service]
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=/bin/sh -c '\
+-if @sbindir@/zpool status %i | grep -q "(trimming)"; then\
+-exec @sbindir@/zpool wait -t trim %i;\
+-else exec @sbindir@/zpool trim -w %i; fi'
+-ExecStop=-/bin/sh -c '@sbindir@/zpool trim -s %i 2>/dev/null || true'
++ExecStart=sh -c '\
++if zpool status %i | grep -q "(trimming)"; then\
++exec zpool wait -t trim %i;\
++else exec zpool trim -w %i; fi'
++ExecStop=-sh -c 'zpool trim -s %i 2>/dev/null || true'
+--- a/etc/systemd/system/zfs-volume-wait.service.in
++++ b/etc/systemd/system/zfs-volume-wait.service.in
+@@ -9,7 +9,7 @@
+ Type=oneshot
+ RemainAfterExit=yes
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=@bindir@/zvol_wait
++ExecStart=zvol_wait
+ 
+ [Install]
+ WantedBy=zfs-volumes.target
diff -pruN 2.3.4-1/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch
--- 2.3.4-1/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch	2024-09-05 02:37:33.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,73 +0,0 @@
-From b2fc7357863095f306f3a03eef460158158f7875 Mon Sep 17 00:00:00 2001
-From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
-Date: Mon, 17 Jan 2022 13:57:28 +0000
-Subject: [PATCH 1/2] Revert "etc/systemd/zfs-mount-generator: output tweaks"
-
-This reverts commit ec3b25825e64f37f74605c451cfc026c28920715.
-
-This is to continue support for ubuntu specific zsys patch that
-currently relies on the shell based implementation of the
-generator. See https://bugs.launchpad.net/bugs/1958142
-
-Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
----
- .../system-generators/zfs-mount-generator.c   | 22 ++++++++++++-------
- 1 file changed, 14 insertions(+), 8 deletions(-)
-
-diff --git a/etc/systemd/system-generators/zfs-mount-generator.c b/etc/systemd/system-generators/zfs-mount-generator.c
-index b806339deb..8deeed9df0 100644
---- a/etc/systemd/system-generators/zfs-mount-generator.c
-+++ b/etc/systemd/system-generators/zfs-mount-generator.c
-@@ -281,7 +281,7 @@ line_worker(char *line, const char *cachefile)
- 
- 	if (strcmp(p_encroot, "-") != 0) {
- 		char *keyloadunit =
--		    systemd_escape(p_encroot, "zfs-load-key@", ".service");
-+		    systemd_escape(p_encroot, "zfs-load-key-", ".service");
- 
- 		if (strcmp(dataset, p_encroot) == 0) {
- 			const char *keymountdep = NULL;
-@@ -360,27 +360,33 @@ line_worker(char *line, const char *cachefile)
- 			    "# dataset is a parent of the root filesystem.\n"
- 			    "StandardOutput=null\n"
- 			    "StandardError=null\n"
--			    "ExecStart=/bin/sh -euc '"
--			        "[ \"$$(" ZFS " get -H -o value keystatus \"%s\")\" = \"unavailable\" ] || exit 0;",
-+			    "ExecStart=/bin/sh -c '"
-+			        "set -eu;"
-+			        "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
-+			        "[ \"$$keystatus\" = \"unavailable\" ] || exit 0;",
- 			    dataset);
- 			if (is_prompt)
- 				fprintf(keyloadunit_f,
--				    "for i in 1 2 3; do "
-+				    "count=0;"
-+				    "while [ $$count -lt 3 ]; do "
- 				        "systemd-ask-password --id=\"zfs:%s\" \"Enter passphrase for %s:\" |"
- 				        "" ZFS " load-key \"%s\" && exit 0;"
-+				        "count=$$((count + 1));"
- 				    "done;"
- 				    "exit 1",
- 				    dataset, dataset, dataset);
- 			else
- 				fprintf(keyloadunit_f,
--				    "exec " ZFS " load-key \"%s\"",
-+				    "" ZFS " load-key \"%s\"",
- 				    dataset);
- 
- 			fprintf(keyloadunit_f,
- 				"'\n"
--				"ExecStop=/bin/sh -euc '"
--				    "[ \"$$(" ZFS " get -H -o value keystatus \"%s\")\" = \"available\" ] || exit 0;"
--				    "exec " ZFS " unload-key \"%s\""
-+				"ExecStop=/bin/sh -c '"
-+				    "set -eu;"
-+				    "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
-+				    "[ \"$$keystatus\" = \"available\" ] || exit 0;"
-+				    "" ZFS " unload-key \"%s\""
- 				"'\n",
- 				dataset, dataset);
- 			/* END CSTYLED */
--- 
-2.32.0
-
diff -pruN 2.3.4-1/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-serialise-han.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-serialise-han.patch
--- 2.3.4-1/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-serialise-han.patch	2024-09-05 02:37:33.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0001-Revert-etc-systemd-zfs-mount-generator-serialise-han.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,780 +0,0 @@
-From 6e60b28bc979477ba8d48347dd556acc5296bb25 Mon Sep 17 00:00:00 2001
-From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
-Date: Tue, 19 Apr 2022 15:58:46 +0100
-Subject: [PATCH 1/3] Revert "etc/systemd/zfs-mount-generator: serialise,
- handle keylocation=http[s]://"
-
-This reverts commit fe6f2651f55de3bf68ac4729386b5e85aa23a447.
-
-This is to continue support for ubuntu specific zsys patch that
-currently relies on the shell based implementation of the
-generator. See https://bugs.launchpad.net/bugs/1958142
-
-Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
----
- .../system-generators/zfs-mount-generator.c   | 437 +++++++++++-------
- man/man8/zfs-mount-generator.8.in             |  19 +-
- 2 files changed, 275 insertions(+), 181 deletions(-)
-
-diff --git a/etc/systemd/system-generators/zfs-mount-generator.c b/etc/systemd/system-generators/zfs-mount-generator.c
-index f4c6c26a0b..b806339deb 100644
---- a/etc/systemd/system-generators/zfs-mount-generator.c
-+++ b/etc/systemd/system-generators/zfs-mount-generator.c
-@@ -27,6 +27,9 @@
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/stat.h>
-+#include <sys/wait.h>
-+#include <sys/mman.h>
-+#include <semaphore.h>
- #include <stdbool.h>
- #include <unistd.h>
- #include <fcntl.h>
-@@ -41,16 +44,25 @@
- #include <errno.h>
- #include <libzfs.h>
- 
--/*
-- * For debugging only.
-- *
-- * Free statics with trivial life-times,
-- * but saved line filenames are replaced with a static string.
-- */
--#define	FREE_STATICS false
--
--#define	nitems(arr) (sizeof (arr) / sizeof (*arr))
- #define	STRCMP ((int(*)(const void *, const void *))&strcmp)
-+#define	PID_T_CMP ((int(*)(const void *, const void *))&pid_t_cmp)
-+
-+static int
-+pid_t_cmp(const pid_t *lhs, const pid_t *rhs)
-+{
-+	/*
-+	 * This is always valid, quoth sys_types.h(7posix):
-+	 * > blksize_t, pid_t, and ssize_t shall be signed integer types.
-+	 */
-+	return (*lhs - *rhs);
-+}
-+
-+#define	EXIT_ENOMEM() \
-+	do { \
-+		fprintf(stderr, PROGNAME "[%d]: " \
-+		    "not enough memory (L%d)!\n", getpid(), __LINE__); \
-+		_exit(1); \
-+	} while (0)
- 
- 
- #define	PROGNAME "zfs-mount-generator"
-@@ -68,11 +80,20 @@
- #define	URI_REGEX_S "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):\\/\\/\\(.*\\)$"
- static regex_t uri_regex;
- 
-+static char *argv0;
-+
- static const char *destdir = "/tmp";
- static int destdir_fd = -1;
- 
- static void *known_pools = NULL; /* tsearch() of C strings */
--static void *noauto_files = NULL; /* tsearch() of C strings */
-+static struct {
-+	sem_t noauto_not_on_sem;
-+
-+	sem_t noauto_names_sem;
-+	size_t noauto_names_len;
-+	size_t noauto_names_max;
-+	char noauto_names[][NAME_MAX];
-+} *noauto_files;
- 
- 
- static char *
-@@ -82,12 +103,8 @@ systemd_escape(const char *input, const char *prepend, const char *append)
- 	size_t applen = strlen(append);
- 	size_t prelen = strlen(prepend);
- 	char *ret = malloc(4 * len + prelen + applen + 1);
--	if (!ret) {
--		fprintf(stderr, PROGNAME "[%d]: "
--		    "out of memory to escape \"%s%s%s\"!\n",
--		    getpid(), prepend, input, append);
--		return (NULL);
--	}
-+	if (!ret)
-+		EXIT_ENOMEM();
- 
- 	memcpy(ret, prepend, prelen);
- 	char *out = ret + prelen;
-@@ -149,12 +166,8 @@ systemd_escape_path(char *input, const char *prepend, const char *append)
- {
- 	if (strcmp(input, "/") == 0) {
- 		char *ret;
--		if (asprintf(&ret, "%s-%s", prepend, append) == -1) {
--			fprintf(stderr, PROGNAME "[%d]: "
--			    "out of memory to escape \"%s%s%s\"!\n",
--			    getpid(), prepend, input, append);
--			ret = NULL;
--		}
-+		if (asprintf(&ret, "%s-%s", prepend, append) == -1)
-+			EXIT_ENOMEM();
- 		return (ret);
- 	} else {
- 		/*
-@@ -196,10 +209,6 @@ fopenat(int dirfd, const char *pathname, int flags,
- static int
- line_worker(char *line, const char *cachefile)
- {
--	int ret = 0;
--	void *tofree_all[8];
--	void **tofree = tofree_all;
--
- 	char *toktmp;
- 	/* BEGIN CSTYLED */
- 	const char *dataset                     = strtok_r(line, "\t", &toktmp);
-@@ -231,9 +240,11 @@ line_worker(char *line, const char *cachefile)
- 	if (p_nbmand == NULL) {
- 		fprintf(stderr, PROGNAME "[%d]: %s: not enough tokens!\n",
- 		    getpid(), dataset);
--		goto err;
-+		return (1);
- 	}
- 
-+	strncpy(argv0, dataset, strlen(argv0));
-+
- 	/* Minimal pre-requisites to mount a ZFS dataset */
- 	const char *after = "zfs-import.target";
- 	const char *wants = "zfs-import.target";
-@@ -269,31 +280,28 @@ line_worker(char *line, const char *cachefile)
- 
- 
- 	if (strcmp(p_encroot, "-") != 0) {
--		char *keyloadunit = *(tofree++) =
-+		char *keyloadunit =
- 		    systemd_escape(p_encroot, "zfs-load-key@", ".service");
--		if (keyloadunit == NULL)
--			goto err;
- 
- 		if (strcmp(dataset, p_encroot) == 0) {
- 			const char *keymountdep = NULL;
- 			bool is_prompt = false;
--			bool need_network = false;
- 
- 			regmatch_t uri_matches[3];
- 			if (regexec(&uri_regex, p_keyloc,
--			    nitems(uri_matches), uri_matches, 0) == 0) {
--				p_keyloc[uri_matches[1].rm_eo] = '\0';
-+			    sizeof (uri_matches) / sizeof (*uri_matches),
-+			    uri_matches, 0) == 0) {
- 				p_keyloc[uri_matches[2].rm_eo] = '\0';
--				const char *scheme =
--				    &p_keyloc[uri_matches[1].rm_so];
- 				const char *path =
- 				    &p_keyloc[uri_matches[2].rm_so];
- 
--				if (strcmp(scheme, "https") == 0 ||
--				    strcmp(scheme, "http") == 0)
--					need_network = true;
--				else
--					keymountdep = path;
-+				/*
-+				 * Assumes all URI keylocations need
-+				 * the mount for their path;
-+				 * http://, for example, wouldn't
-+				 * (but it'd need network-online.target et al.)
-+				 */
-+				keymountdep = path;
- 			} else {
- 				if (strcmp(p_keyloc, "prompt") != 0)
- 					fprintf(stderr, PROGNAME "[%d]: %s: "
-@@ -313,7 +321,7 @@ line_worker(char *line, const char *cachefile)
- 				    "couldn't open %s under %s: %s\n",
- 				    getpid(), dataset, keyloadunit, destdir,
- 				    strerror(errno));
--				goto err;
-+				return (1);
- 			}
- 
- 			fprintf(keyloadunit_f,
-@@ -327,22 +335,20 @@ line_worker(char *line, const char *cachefile)
- 			    "After=%s\n",
- 			    dataset, cachefile, wants, after);
- 
--			if (need_network)
--				fprintf(keyloadunit_f,
--				    "Wants=network-online.target\n"
--				    "After=network-online.target\n");
--
- 			if (p_systemd_requires)
- 				fprintf(keyloadunit_f,
- 				    "Requires=%s\n", p_systemd_requires);
- 
--			if (p_systemd_requiresmountsfor)
--				fprintf(keyloadunit_f,
--				    "RequiresMountsFor=%s\n",
--				    p_systemd_requiresmountsfor);
--			if (keymountdep)
--				fprintf(keyloadunit_f,
--				    "RequiresMountsFor='%s'\n", keymountdep);
-+			if (p_systemd_requiresmountsfor || keymountdep) {
-+				fprintf(keyloadunit_f, "RequiresMountsFor=");
-+				if (p_systemd_requiresmountsfor)
-+					fprintf(keyloadunit_f,
-+					    "%s ", p_systemd_requiresmountsfor);
-+				if (keymountdep)
-+					fprintf(keyloadunit_f,
-+					    "'%s'", keymountdep);
-+				fprintf(keyloadunit_f, "\n");
-+			}
- 
- 			/* BEGIN CSTYLED */
- 			fprintf(keyloadunit_f,
-@@ -387,13 +393,9 @@ line_worker(char *line, const char *cachefile)
- 		if (after[0] == '\0')
- 			after = keyloadunit;
- 		else if (asprintf(&toktmp, "%s %s", after, keyloadunit) != -1)
--			after = *(tofree++) = toktmp;
--		else {
--			fprintf(stderr, PROGNAME "[%d]: %s: "
--			    "out of memory to generate after=\"%s %s\"!\n",
--			    getpid(), dataset, after, keyloadunit);
--			goto err;
--		}
-+			after = toktmp;
-+		else
-+			EXIT_ENOMEM();
- 	}
- 
- 
-@@ -402,12 +404,12 @@ line_worker(char *line, const char *cachefile)
- 	    strcmp(p_systemd_ignore, "off") == 0) {
- 		/* ok */
- 	} else if (strcmp(p_systemd_ignore, "on") == 0)
--		goto end;
-+		return (0);
- 	else {
- 		fprintf(stderr, PROGNAME "[%d]: %s: "
- 		    "invalid org.openzfs.systemd:ignore=%s\n",
- 		    getpid(), dataset, p_systemd_ignore);
--		goto err;
-+		return (1);
- 	}
- 
- 	/* Check for canmount */
-@@ -416,21 +418,21 @@ line_worker(char *line, const char *cachefile)
- 	} else if (strcmp(p_canmount, "noauto") == 0)
- 		noauto = true;
- 	else if (strcmp(p_canmount, "off") == 0)
--		goto end;
-+		return (0);
- 	else {
- 		fprintf(stderr, PROGNAME "[%d]: %s: invalid canmount=%s\n",
- 		    getpid(), dataset, p_canmount);
--		goto err;
-+		return (1);
- 	}
- 
- 	/* Check for legacy and blank mountpoints */
- 	if (strcmp(p_mountpoint, "legacy") == 0 ||
- 	    strcmp(p_mountpoint, "none") == 0)
--		goto end;
-+		return (0);
- 	else if (p_mountpoint[0] != '/') {
- 		fprintf(stderr, PROGNAME "[%d]: %s: invalid mountpoint=%s\n",
- 		    getpid(), dataset, p_mountpoint);
--		goto err;
-+		return (1);
- 	}
- 
- 	/* Escape the mountpoint per systemd policy */
-@@ -440,7 +442,7 @@ line_worker(char *line, const char *cachefile)
- 		fprintf(stderr,
- 		    PROGNAME "[%d]: %s: abnormal simplified mountpoint: %s\n",
- 		    getpid(), dataset, p_mountpoint);
--		goto err;
-+		return (1);
- 	}
- 
- 
-@@ -550,7 +552,8 @@ line_worker(char *line, const char *cachefile)
- 	 * 	files if we're sure they were created by us. (see 5.)
- 	 * 2.	We handle files differently based on canmount.
- 	 * 	Units with canmount=on always have precedence over noauto.
--	 * 	This is enforced by processing these units before all others.
-+	 * 	This is enforced by the noauto_not_on_sem semaphore,
-+	 * 	which is only unlocked when the last canmount=on process exits.
- 	 * 	It is important to use p_canmount and not noauto here,
- 	 * 	since we categorise by canmount while other properties,
- 	 * 	e.g. org.openzfs.systemd:wanted-by, also modify noauto.
-@@ -558,7 +561,7 @@ line_worker(char *line, const char *cachefile)
- 	 * 	Additionally, we use noauto_files to track the unit file names
- 	 * 	(which are the systemd-escaped mountpoints) of all (exclusively)
- 	 * 	noauto datasets that had a file created.
--	 * 4.	If the file to be created is found in the tracking tree,
-+	 * 4.	If the file to be created is found in the tracking array,
- 	 * 	we do NOT create it.
- 	 * 5.	If a file exists for a noauto dataset,
- 	 * 	we check whether the file name is in the array.
-@@ -568,14 +571,29 @@ line_worker(char *line, const char *cachefile)
- 	 * 	further noauto datasets creating a file for this path again.
- 	 */
- 
-+	{
-+		sem_t *our_sem = (strcmp(p_canmount, "on") == 0) ?
-+		    &noauto_files->noauto_names_sem :
-+		    &noauto_files->noauto_not_on_sem;
-+		while (sem_wait(our_sem) == -1 && errno == EINTR)
-+			;
-+	}
-+
- 	struct stat stbuf;
- 	bool already_exists = fstatat(destdir_fd, mountfile, &stbuf, 0) == 0;
--	bool is_known = tfind(mountfile, &noauto_files, STRCMP) != NULL;
- 
--	*(tofree++) = (void *)mountfile;
-+	bool is_known = false;
-+	for (size_t i = 0; i < noauto_files->noauto_names_len; ++i) {
-+		if (strncmp(
-+		    noauto_files->noauto_names[i], mountfile, NAME_MAX) == 0) {
-+			is_known = true;
-+			break;
-+		}
-+	}
-+
- 	if (already_exists) {
- 		if (is_known) {
--			/* If it's in noauto_files, we must be noauto too */
-+			/* If it's in $noauto_files, we must be noauto too */
- 
- 			/* See 5 */
- 			errno = 0;
-@@ -596,31 +614,43 @@ line_worker(char *line, const char *cachefile)
- 		}
- 
- 		/* File exists: skip current dataset */
--		goto end;
-+		if (strcmp(p_canmount, "on") == 0)
-+			sem_post(&noauto_files->noauto_names_sem);
-+		return (0);
- 	} else {
- 		if (is_known) {
- 			/* See 4 */
--			goto end;
-+			if (strcmp(p_canmount, "on") == 0)
-+				sem_post(&noauto_files->noauto_names_sem);
-+			return (0);
- 		} else if (strcmp(p_canmount, "noauto") == 0) {
--			if (tsearch(mountfile, &noauto_files, STRCMP) == NULL)
-+			if (noauto_files->noauto_names_len ==
-+			    noauto_files->noauto_names_max)
- 				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "out of memory for noauto datasets! "
--				    "Not tracking %s.\n",
--				    getpid(), dataset, mountfile);
--			else
--				/* mountfile escaped to noauto_files */
--				*(--tofree) = NULL;
-+				    "noauto dataset limit (%zu) reached! "
-+				    "Not tracking %s. Please report this to "
-+				    "https://github.com/openzfs/zfs\n",
-+				    getpid(), dataset,
-+				    noauto_files->noauto_names_max, mountfile);
-+			else {
-+				strncpy(noauto_files->noauto_names[
-+				    noauto_files->noauto_names_len],
-+				    mountfile, NAME_MAX);
-+				++noauto_files->noauto_names_len;
-+			}
- 		}
- 	}
- 
- 
- 	FILE *mountfile_f = fopenat(destdir_fd, mountfile,
- 	    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w", 0644);
-+	if (strcmp(p_canmount, "on") == 0)
-+		sem_post(&noauto_files->noauto_names_sem);
- 	if (!mountfile_f) {
- 		fprintf(stderr,
- 		    PROGNAME "[%d]: %s: couldn't open %s under %s: %s\n",
- 		    getpid(), dataset, mountfile, destdir, strerror(errno));
--		goto err;
-+		return (1);
- 	}
- 
- 	fprintf(mountfile_f,
-@@ -669,17 +699,12 @@ line_worker(char *line, const char *cachefile)
- 	(void) fclose(mountfile_f);
- 
- 	if (!requiredby && !wantedby)
--		goto end;
-+		return (0);
- 
- 	/* Finally, create the appropriate dependencies */
- 	char *linktgt;
--	if (asprintf(&linktgt, "../%s", mountfile) == -1) {
--		fprintf(stderr, PROGNAME "[%d]: %s: "
--		    "out of memory for dependents of %s!\n",
--		    getpid(), dataset, mountfile);
--		goto err;
--	}
--	*(tofree++) = linktgt;
-+	if (asprintf(&linktgt, "../%s", mountfile) == -1)
-+		EXIT_ENOMEM();
- 
- 	char *dependencies[][2] = {
- 		{"wants", wantedby},
-@@ -694,14 +719,8 @@ line_worker(char *line, const char *cachefile)
- 		    reqby;
- 		    reqby = strtok_r(NULL, " ", &toktmp)) {
- 			char *depdir;
--			if (asprintf(
--			    &depdir, "%s.%s", reqby, (*dep)[0]) == -1) {
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "out of memory for dependent dir name "
--				    "\"%s.%s\"!\n",
--				    getpid(), dataset, reqby, (*dep)[0]);
--				continue;
--			}
-+			if (asprintf(&depdir, "%s.%s", reqby, (*dep)[0]) == -1)
-+				EXIT_ENOMEM();
- 
- 			(void) mkdirat(destdir_fd, depdir, 0755);
- 			int depdir_fd = openat(destdir_fd, depdir,
-@@ -727,24 +746,7 @@ line_worker(char *line, const char *cachefile)
- 		}
- 	}
- 
--end:
--	if (tofree >= tofree_all + nitems(tofree_all)) {
--		/*
--		 * This won't happen as-is:
--		 * we've got 8 slots and allocate 4 things at most.
--		 */
--		fprintf(stderr,
--		    PROGNAME "[%d]: %s: need to free %zu > %zu!\n",
--		    getpid(), dataset, tofree - tofree_all, nitems(tofree_all));
--		ret = tofree - tofree_all;
--	}
--
--	while (tofree-- != tofree_all)
--		free(*tofree);
--	return (ret);
--err:
--	ret = 1;
--	goto end;
-+	return (0);
- }
- 
- 
-@@ -778,11 +780,12 @@ main(int argc, char **argv)
- 		if (kmfd >= 0) {
- 			(void) dup2(kmfd, STDERR_FILENO);
- 			(void) close(kmfd);
--
--			setlinebuf(stderr);
- 		}
- 	}
- 
-+	uint8_t debug = 0;
-+
-+	argv0 = argv[0];
- 	switch (argc) {
- 	case 1:
- 		/* Use default */
-@@ -841,9 +844,33 @@ main(int argc, char **argv)
- 		}
- 	}
- 
--	bool debug = false;
-+	{
-+		/*
-+		 * We could just get a gigabyte here and Not Care,
-+		 * but if vm.overcommit_memory=2, then MAP_NORESERVE is ignored
-+		 * and we'd try (and likely fail) to rip it out of swap
-+		 */
-+		noauto_files = mmap(NULL, 4 * 1024 * 1024,
-+		    PROT_READ | PROT_WRITE,
-+		    MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
-+		if (noauto_files == MAP_FAILED) {
-+			fprintf(stderr,
-+			    PROGNAME "[%d]: couldn't allocate IPC region: %s\n",
-+			    getpid(), strerror(errno));
-+			_exit(1);
-+		}
-+
-+		sem_init(&noauto_files->noauto_not_on_sem, true, 0);
-+		sem_init(&noauto_files->noauto_names_sem, true, 1);
-+		noauto_files->noauto_names_len = 0;
-+		/* Works out to 16447ish, *well* enough */
-+		noauto_files->noauto_names_max =
-+		    (4 * 1024 * 1024 - sizeof (*noauto_files)) / NAME_MAX;
-+	}
-+
- 	char *line = NULL;
- 	size_t linelen = 0;
-+	struct timespec time_start = {};
- 	{
- 		const char *dbgenv = getenv("ZFS_DEBUG");
- 		if (dbgenv)
-@@ -852,7 +879,7 @@ main(int argc, char **argv)
- 			FILE *cmdline = fopen("/proc/cmdline", "re");
- 			if (cmdline != NULL) {
- 				if (getline(&line, &linelen, cmdline) >= 0)
--					debug = strstr(line, "debug");
-+					debug = strstr(line, "debug") ? 2 : 0;
- 				(void) fclose(cmdline);
- 			}
- 		}
-@@ -861,17 +888,19 @@ main(int argc, char **argv)
- 			dup2(STDERR_FILENO, STDOUT_FILENO);
- 	}
- 
--	struct timespec time_start = {};
-+	size_t forked_canmount_on = 0;
-+	size_t forked_canmount_not_on = 0;
-+	size_t canmount_on_pids_len = 128;
-+	pid_t *canmount_on_pids =
-+	    malloc(canmount_on_pids_len * sizeof (*canmount_on_pids));
-+	if (canmount_on_pids == NULL)
-+		canmount_on_pids_len = 0;
-+
- 	if (debug)
- 		clock_gettime(CLOCK_MONOTONIC_RAW, &time_start);
- 
--	struct line {
--		char *line;
--		const char *fname;
--		struct line *next;
--	} *lines_canmount_not_on = NULL;
--
--	int ret = 0;
-+	ssize_t read;
-+	pid_t pid;
- 	struct dirent *cachent;
- 	while ((cachent = readdir(fslist_dir)) != NULL) {
- 		if (strcmp(cachent->d_name, ".") == 0 ||
-@@ -887,67 +916,129 @@ main(int argc, char **argv)
- 			continue;
- 		}
- 
--		const char *filename = FREE_STATICS ? "(elided)" : NULL;
--
--		ssize_t read;
- 		while ((read = getline(&line, &linelen, cachefile)) >= 0) {
- 			line[read - 1] = '\0'; /* newline */
- 
--			char *canmount = line;
--			canmount += strcspn(canmount, "\t");
--			canmount += strspn(canmount, "\t");
--			canmount += strcspn(canmount, "\t");
--			canmount += strspn(canmount, "\t");
--			bool canmount_on = strncmp(canmount, "on", 2) == 0;
--
--			if (canmount_on)
--				ret |= line_worker(line, cachent->d_name);
--			else {
--				if (filename == NULL)
--					filename =
--					    strdup(cachent->d_name) ?: "(?)";
--
--				struct line *l = calloc(1, sizeof (*l));
--				char *nl = strdup(line);
--				if (l == NULL || nl == NULL) {
--					fprintf(stderr, PROGNAME "[%d]: "
--					    "out of memory for \"%s\" in %s\n",
--					    getpid(), line, cachent->d_name);
--					free(l);
--					free(nl);
--					continue;
-+			switch (pid = fork()) {
-+			case -1:
-+				fprintf(stderr,
-+				    PROGNAME "[%d]: couldn't fork for %s: %s\n",
-+				    getpid(), line, strerror(errno));
-+				break;
-+			case 0: /* child */
-+				_exit(line_worker(line, cachent->d_name));
-+			default: { /* parent */
-+				char *tmp;
-+				char *dset = strtok_r(line, "\t", &tmp);
-+				strtok_r(NULL, "\t", &tmp);
-+				char *canmount = strtok_r(NULL, "\t", &tmp);
-+				bool canmount_on =
-+				    canmount && strncmp(canmount, "on", 2) == 0;
-+
-+				if (debug >= 2)
-+					printf(PROGNAME ": forked %d, "
-+					    "canmount_on=%d, dataset=%s\n",
-+					    (int)pid, canmount_on, dset);
-+
-+				if (canmount_on &&
-+				    forked_canmount_on ==
-+				    canmount_on_pids_len) {
-+					size_t new_len =
-+					    (canmount_on_pids_len ?: 16) * 2;
-+					void *new_pidlist =
-+					    realloc(canmount_on_pids,
-+					    new_len *
-+					    sizeof (*canmount_on_pids));
-+					if (!new_pidlist) {
-+						fprintf(stderr,
-+						    PROGNAME "[%d]: "
-+						    "out of memory! "
-+						    "Mount ordering may be "
-+						    "affected.\n", getpid());
-+						continue;
-+					}
-+
-+					canmount_on_pids = new_pidlist;
-+					canmount_on_pids_len = new_len;
- 				}
--				l->line = nl;
--				l->fname = filename;
--				l->next = lines_canmount_not_on;
--				lines_canmount_not_on = l;
-+
-+				if (canmount_on) {
-+					canmount_on_pids[forked_canmount_on] =
-+					    pid;
-+					++forked_canmount_on;
-+				} else
-+					++forked_canmount_not_on;
-+				break;
-+			}
- 			}
- 		}
- 
--		fclose(cachefile);
-+		(void) fclose(cachefile);
- 	}
- 	free(line);
- 
--	while (lines_canmount_not_on) {
--		struct line *l = lines_canmount_not_on;
--		lines_canmount_not_on = l->next;
-+	if (forked_canmount_on == 0) {
-+		/* No canmount=on processes to finish, so don't deadlock here */
-+		for (size_t i = 0; i < forked_canmount_not_on; ++i)
-+			sem_post(&noauto_files->noauto_not_on_sem);
-+	} else {
-+		/* Likely a no-op, since we got these from a narrow fork loop */
-+		qsort(canmount_on_pids, forked_canmount_on,
-+		    sizeof (*canmount_on_pids), PID_T_CMP);
-+	}
- 
--		ret |= line_worker(l->line, l->fname);
--		if (FREE_STATICS) {
--			free(l->line);
--			free(l);
-+	int status, ret = 0;
-+	struct rusage usage;
-+	size_t forked_canmount_on_max = forked_canmount_on;
-+	while ((pid = wait4(-1, &status, 0, &usage)) != -1) {
-+		ret |= WEXITSTATUS(status) | WTERMSIG(status);
-+
-+		if (forked_canmount_on != 0) {
-+			if (bsearch(&pid, canmount_on_pids,
-+			    forked_canmount_on_max, sizeof (*canmount_on_pids),
-+			    PID_T_CMP))
-+				--forked_canmount_on;
-+
-+			if (forked_canmount_on == 0) {
-+				/*
-+				 * All canmount=on processes have finished,
-+				 * let all the lower-priority ones finish now
-+				 */
-+				for (size_t i = 0;
-+				    i < forked_canmount_not_on; ++i)
-+					sem_post(
-+					    &noauto_files->noauto_not_on_sem);
-+			}
- 		}
-+
-+		if (debug >= 2)
-+			printf(PROGNAME ": %d done, user=%llu.%06us, "
-+			    "system=%llu.%06us, maxrss=%ldB, ex=0x%x\n",
-+			    (int)pid,
-+			    (unsigned long long) usage.ru_utime.tv_sec,
-+			    (unsigned int) usage.ru_utime.tv_usec,
-+			    (unsigned long long) usage.ru_stime.tv_sec,
-+			    (unsigned int) usage.ru_stime.tv_usec,
-+			    usage.ru_maxrss * 1024, status);
- 	}
- 
- 	if (debug) {
- 		struct timespec time_end = {};
- 		clock_gettime(CLOCK_MONOTONIC_RAW, &time_end);
- 
--		struct rusage usage;
- 		getrusage(RUSAGE_SELF, &usage);
- 		printf(
- 		    "\n"
--		    PROGNAME ": "
-+		    PROGNAME ": self    : "
-+		    "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
-+		    (unsigned long long) usage.ru_utime.tv_sec,
-+		    (unsigned int) usage.ru_utime.tv_usec,
-+		    (unsigned long long) usage.ru_stime.tv_sec,
-+		    (unsigned int) usage.ru_stime.tv_usec,
-+		    usage.ru_maxrss * 1024);
-+
-+		getrusage(RUSAGE_CHILDREN, &usage);
-+		printf(PROGNAME ": children: "
- 		    "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
- 		    (unsigned long long) usage.ru_utime.tv_sec,
- 		    (unsigned int) usage.ru_utime.tv_usec,
-@@ -977,7 +1068,7 @@ main(int argc, char **argv)
- 		    time_init.tv_nsec / 1000000000;
- 		time_init.tv_nsec %= 1000000000;
- 
--		printf(PROGNAME ": "
-+		printf(PROGNAME ": wall    : "
- 		    "total=%llu.%09llus = "
- 		    "init=%llu.%09llus + real=%llu.%09llus\n",
- 		    (unsigned long long) time_init.tv_sec,
-@@ -986,15 +1077,7 @@ main(int argc, char **argv)
- 		    (unsigned long long) time_start.tv_nsec,
- 		    (unsigned long long) time_end.tv_sec,
- 		    (unsigned long long) time_end.tv_nsec);
--
--		fflush(stdout);
- 	}
- 
--	if (FREE_STATICS) {
--		closedir(fslist_dir);
--		tdestroy(noauto_files, free);
--		tdestroy(known_pools, free);
--		regfree(&uri_regex);
--	}
- 	_exit(ret);
- }
-diff --git a/man/man8/zfs-mount-generator.8.in b/man/man8/zfs-mount-generator.8.in
-index ae8937038e..7aa332ba81 100644
---- a/man/man8/zfs-mount-generator.8.in
-+++ b/man/man8/zfs-mount-generator.8.in
-@@ -142,11 +142,22 @@ ZEDLET, if enabled
- .Pq see Xr zed 8 .
- .
- .Sh ENVIRONMENT
--If the
-+The
- .Sy ZFS_DEBUG
--environment variable is nonzero
--.Pq or unset and Pa /proc/cmdline No contains Qq Sy debug ,
--print summary accounting information at the end.
-+environment variable can either be
-+.Sy 0
-+(default),
-+.Sy 1
-+(print summary accounting information at the end), or at least
-+.Sy 2
-+(print accounting information for each subprocess as it finishes).
-+.
-+If not present,
-+.Pa /proc/cmdline
-+is additionally checked for
-+.Qq debug ,
-+in which case the debug level is set to
-+.Sy 2 .
- .
- .Sh EXAMPLES
- To begin, enable tracking for the pool:
--- 
-2.32.0
-
diff -pruN 2.3.4-1/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch
--- 2.3.4-1/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch	2024-09-05 02:37:33.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-output-tweaks.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,73 +0,0 @@
-From 83fbd45b461f5ddec2eedfe96ea4375452637406 Mon Sep 17 00:00:00 2001
-From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
-Date: Mon, 17 Jan 2022 13:57:28 +0000
-Subject: [PATCH 2/3] Revert "etc/systemd/zfs-mount-generator: output tweaks"
-
-This reverts commit ec3b25825e64f37f74605c451cfc026c28920715.
-
-This is to continue support for ubuntu specific zsys patch that
-currently relies on the shell based implementation of the
-generator. See https://bugs.launchpad.net/bugs/1958142
-
-Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
----
- .../system-generators/zfs-mount-generator.c   | 22 ++++++++++++-------
- 1 file changed, 14 insertions(+), 8 deletions(-)
-
-diff --git a/etc/systemd/system-generators/zfs-mount-generator.c b/etc/systemd/system-generators/zfs-mount-generator.c
-index b806339deb..8deeed9df0 100644
---- a/etc/systemd/system-generators/zfs-mount-generator.c
-+++ b/etc/systemd/system-generators/zfs-mount-generator.c
-@@ -281,7 +281,7 @@ line_worker(char *line, const char *cachefile)
- 
- 	if (strcmp(p_encroot, "-") != 0) {
- 		char *keyloadunit =
--		    systemd_escape(p_encroot, "zfs-load-key@", ".service");
-+		    systemd_escape(p_encroot, "zfs-load-key-", ".service");
- 
- 		if (strcmp(dataset, p_encroot) == 0) {
- 			const char *keymountdep = NULL;
-@@ -360,27 +360,33 @@ line_worker(char *line, const char *cachefile)
- 			    "# dataset is a parent of the root filesystem.\n"
- 			    "StandardOutput=null\n"
- 			    "StandardError=null\n"
--			    "ExecStart=/bin/sh -euc '"
--			        "[ \"$$(" ZFS " get -H -o value keystatus \"%s\")\" = \"unavailable\" ] || exit 0;",
-+			    "ExecStart=/bin/sh -c '"
-+			        "set -eu;"
-+			        "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
-+			        "[ \"$$keystatus\" = \"unavailable\" ] || exit 0;",
- 			    dataset);
- 			if (is_prompt)
- 				fprintf(keyloadunit_f,
--				    "for i in 1 2 3; do "
-+				    "count=0;"
-+				    "while [ $$count -lt 3 ]; do "
- 				        "systemd-ask-password --id=\"zfs:%s\" \"Enter passphrase for %s:\" |"
- 				        "" ZFS " load-key \"%s\" && exit 0;"
-+				        "count=$$((count + 1));"
- 				    "done;"
- 				    "exit 1",
- 				    dataset, dataset, dataset);
- 			else
- 				fprintf(keyloadunit_f,
--				    "exec " ZFS " load-key \"%s\"",
-+				    "" ZFS " load-key \"%s\"",
- 				    dataset);
- 
- 			fprintf(keyloadunit_f,
- 				"'\n"
--				"ExecStop=/bin/sh -euc '"
--				    "[ \"$$(" ZFS " get -H -o value keystatus \"%s\")\" = \"available\" ] || exit 0;"
--				    "exec " ZFS " unload-key \"%s\""
-+				"ExecStop=/bin/sh -c '"
-+				    "set -eu;"
-+				    "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
-+				    "[ \"$$keystatus\" = \"available\" ] || exit 0;"
-+				    "" ZFS " unload-key \"%s\""
- 				"'\n",
- 				dataset, dataset);
- 			/* END CSTYLED */
--- 
-2.32.0
-
diff -pruN 2.3.4-1/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch
--- 2.3.4-1/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch	2024-09-05 02:37:33.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0002-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch	2025-09-04 19:31:05.000000000 +0000
@@ -1,7 +1,7 @@
-From 474ddc9fa5440312e5224f394052264ef7644b00 Mon Sep 17 00:00:00 2001
-From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
-Date: Mon, 17 Jan 2022 14:00:42 +0000
-Subject: [PATCH 2/2] Revert "etc/systemd/zfs-mount-generator: rewrite in C"
+From 96e20d3f61225165d806cdd3f65c802e00a3be8f Mon Sep 17 00:00:00 2001
+From: Heitor Alves de Siqueira <halves@canonical.com>
+Date: Mon, 20 Jan 2025 11:37:00 -0300
+Subject: [PATCH] Revert "etc/systemd/zfs-mount-generator: rewrite in C"
 
 This reverts commit 0382362ce06a5514a97bbbf11dfe55e7e408898a.
 
@@ -9,1151 +9,31 @@ This is to continue support for ubuntu s
 currently relies on the shell based implementation of the
 generator. See https://bugs.launchpad.net/bugs/1958142
 
-Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+Last-Update: 2025-01-20
+Signed-off-by: Heitor Alves de Siqueira <halves@canonical.com>
 ---
- Makefile.am                                   |    2 +-
- etc/systemd/system-generators/Makefile.am     |   14 +-
- .../system-generators/zfs-mount-generator.c   | 1089 -----------------
- .../system-generators/zfs-mount-generator.in  |  474 +++++++
- 4 files changed, 478 insertions(+), 1101 deletions(-)
- delete mode 100644 etc/systemd/system-generators/zfs-mount-generator.c
+ etc/systemd/system-generators/Makefile.am     |   6 +
+ .../system-generators/zfs-mount-generator.in  | 474 ++++++++++++++++++
+ man/man8/zfs-mount-generator.8.in             | 331 +++++++-----
+ 3 files changed, 673 insertions(+), 138 deletions(-)
+ create mode 100644 etc/systemd/system-generators/Makefile.am
  create mode 100755 etc/systemd/system-generators/zfs-mount-generator.in
 
-diff --git a/Makefile.am b/Makefile.am
-index 34fe16ce41..d4e69a749c 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -8,7 +8,7 @@ SUBDIRS += rpm
- endif
- 
- if CONFIG_USER
--SUBDIRS += man scripts lib tests cmd etc contrib
-+SUBDIRS += etc man scripts lib tests cmd contrib
- if BUILD_LINUX
- SUBDIRS += udev
- endif
-diff --git a/etc/systemd/system-generators/Makefile.am b/etc/systemd/system-generators/Makefile.am
-index e5920bf392..fee88dad8c 100644
---- a/etc/systemd/system-generators/Makefile.am
-+++ b/etc/systemd/system-generators/Makefile.am
-@@ -1,14 +1,6 @@
--include $(top_srcdir)/config/Rules.am
+Index: zfs-linux/etc/systemd/system-generators/Makefile.am
+===================================================================
+--- /dev/null
++++ zfs-linux/etc/systemd/system-generators/Makefile.am
+@@ -0,0 +1,6 @@
 +include $(top_srcdir)/config/Substfiles.am
- 
--systemdgenerator_PROGRAMS = \
++
 +systemdgenerator_SCRIPTS = \
- 	zfs-mount-generator
- 
--zfs_mount_generator_SOURCES = \
--	zfs-mount-generator.c
--
--zfs_mount_generator_LDADD = \
--	$(abs_top_builddir)/lib/libzfs/libzfs.la
--
--zfs_mount_generator_LDFLAGS = -pthread
--
--include $(top_srcdir)/config/CppCheck.am
++	zfs-mount-generator
++
 +SUBSTFILES += $(systemdgenerator_SCRIPTS)
-diff --git a/etc/systemd/system-generators/zfs-mount-generator.c b/etc/systemd/system-generators/zfs-mount-generator.c
-deleted file mode 100644
-index 8deeed9df0..0000000000
---- a/etc/systemd/system-generators/zfs-mount-generator.c
-+++ /dev/null
-@@ -1,1089 +0,0 @@
--/*
-- * Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
-- * Copyright (c) 2020 InsanePrawn <insane.prawny@gmail.com>
-- *
-- * Permission is hereby granted, free of charge, to any person obtaining
-- * a copy of this software and associated documentation files (the
-- * "Software"), to deal in the Software without restriction, including
-- * without limitation the rights to use, copy, modify, merge, publish,
-- * distribute, sublicense, and/or sell copies of the Software, and to
-- * permit persons to whom the Software is furnished to do so, subject to
-- * the following conditions:
-- *
-- * The above copyright notice and this permission notice shall be
-- * included in all copies or substantial portions of the Software.
-- *
-- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- */
--
--
--#include <sys/resource.h>
--#include <sys/types.h>
--#include <sys/time.h>
--#include <sys/stat.h>
--#include <sys/wait.h>
--#include <sys/mman.h>
--#include <semaphore.h>
--#include <stdbool.h>
--#include <unistd.h>
--#include <fcntl.h>
--#include <stdio.h>
--#include <time.h>
--#include <regex.h>
--#include <search.h>
--#include <dirent.h>
--#include <string.h>
--#include <stdlib.h>
--#include <limits.h>
--#include <errno.h>
--#include <libzfs.h>
--
--#define	STRCMP ((int(*)(const void *, const void *))&strcmp)
--#define	PID_T_CMP ((int(*)(const void *, const void *))&pid_t_cmp)
--
--static int
--pid_t_cmp(const pid_t *lhs, const pid_t *rhs)
--{
--	/*
--	 * This is always valid, quoth sys_types.h(7posix):
--	 * > blksize_t, pid_t, and ssize_t shall be signed integer types.
--	 */
--	return (*lhs - *rhs);
--}
--
--#define	EXIT_ENOMEM() \
--	do { \
--		fprintf(stderr, PROGNAME "[%d]: " \
--		    "not enough memory (L%d)!\n", getpid(), __LINE__); \
--		_exit(1); \
--	} while (0)
--
--
--#define	PROGNAME "zfs-mount-generator"
--#define	FSLIST SYSCONFDIR "/zfs/zfs-list.cache"
--#define	ZFS SBINDIR "/zfs"
--
--#define	OUTPUT_HEADER \
--	"# Automatically generated by " PROGNAME "\n" \
--	"\n"
--
--/*
-- * Starts like the one in libzfs_util.c but also matches "//"
-- * and captures until the end, since we actually use it for path extraxion
-- */
--#define	URI_REGEX_S "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):\\/\\/\\(.*\\)$"
--static regex_t uri_regex;
--
--static char *argv0;
--
--static const char *destdir = "/tmp";
--static int destdir_fd = -1;
--
--static void *known_pools = NULL; /* tsearch() of C strings */
--static struct {
--	sem_t noauto_not_on_sem;
--
--	sem_t noauto_names_sem;
--	size_t noauto_names_len;
--	size_t noauto_names_max;
--	char noauto_names[][NAME_MAX];
--} *noauto_files;
--
--
--static char *
--systemd_escape(const char *input, const char *prepend, const char *append)
--{
--	size_t len = strlen(input);
--	size_t applen = strlen(append);
--	size_t prelen = strlen(prepend);
--	char *ret = malloc(4 * len + prelen + applen + 1);
--	if (!ret)
--		EXIT_ENOMEM();
--
--	memcpy(ret, prepend, prelen);
--	char *out = ret + prelen;
--
--	const char *cur = input;
--	if (*cur == '.') {
--		memcpy(out, "\\x2e", 4);
--		out += 4;
--		++cur;
--	}
--	for (; *cur; ++cur) {
--		if (*cur == '/')
--			*(out++) = '-';
--		else if (strchr(
--		    "0123456789"
--		    "abcdefghijklmnopqrstuvwxyz"
--		    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
--		    ":_.", *cur))
--			*(out++) = *cur;
--		else {
--			sprintf(out, "\\x%02x", (int)*cur);
--			out += 4;
--		}
--	}
--
--	memcpy(out, append, applen + 1);
--	return (ret);
--}
--
--static void
--simplify_path(char *path)
--{
--	char *out = path;
--	for (char *cur = path; *cur; ++cur) {
--		if (*cur == '/') {
--			while (*(cur + 1) == '/')
--				++cur;
--			*(out++) = '/';
--		} else
--			*(out++) = *cur;
--	}
--
--	*(out++) = '\0';
--}
--
--static bool
--strendswith(const char *what, const char *suff)
--{
--	size_t what_l = strlen(what);
--	size_t suff_l = strlen(suff);
--
--	return ((what_l >= suff_l) &&
--	    (strcmp(what + what_l - suff_l, suff) == 0));
--}
--
--/* Assumes already-simplified path, doesn't modify input */
--static char *
--systemd_escape_path(char *input, const char *prepend, const char *append)
--{
--	if (strcmp(input, "/") == 0) {
--		char *ret;
--		if (asprintf(&ret, "%s-%s", prepend, append) == -1)
--			EXIT_ENOMEM();
--		return (ret);
--	} else {
--		/*
--		 * path_is_normalized() (flattened for absolute paths here),
--		 * required for proper escaping
--		 */
--		if (strstr(input, "/./") || strstr(input, "/../") ||
--		    strendswith(input, "/.") || strendswith(input, "/.."))
--			return (NULL);
--
--
--		if (input[0] == '/')
--			++input;
--
--		char *back = &input[strlen(input) - 1];
--		bool deslash = *back == '/';
--		if (deslash)
--			*back = '\0';
--
--		char *ret = systemd_escape(input, prepend, append);
--
--		if (deslash)
--			*back = '/';
--		return (ret);
--	}
--}
--
--static FILE *
--fopenat(int dirfd, const char *pathname, int flags,
--    const char *stream_mode, mode_t mode)
--{
--	int fd = openat(dirfd, pathname, flags, mode);
--	if (fd < 0)
--		return (NULL);
--
--	return (fdopen(fd, stream_mode));
--}
--
--static int
--line_worker(char *line, const char *cachefile)
--{
--	char *toktmp;
--	/* BEGIN CSTYLED */
--	const char *dataset                     = strtok_r(line, "\t", &toktmp);
--	      char *p_mountpoint                = strtok_r(NULL, "\t", &toktmp);
--	const char *p_canmount                  = strtok_r(NULL, "\t", &toktmp);
--	const char *p_atime                     = strtok_r(NULL, "\t", &toktmp);
--	const char *p_relatime                  = strtok_r(NULL, "\t", &toktmp);
--	const char *p_devices                   = strtok_r(NULL, "\t", &toktmp);
--	const char *p_exec                      = strtok_r(NULL, "\t", &toktmp);
--	const char *p_readonly                  = strtok_r(NULL, "\t", &toktmp);
--	const char *p_setuid                    = strtok_r(NULL, "\t", &toktmp);
--	const char *p_nbmand                    = strtok_r(NULL, "\t", &toktmp);
--	const char *p_encroot                   = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	      char *p_keyloc                    = strtok_r(NULL, "\t", &toktmp) ?: strdupa("none");
--	const char *p_systemd_requires          = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_requiresmountsfor = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_before            = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_after             = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	      char *p_systemd_wantedby          = strtok_r(NULL, "\t", &toktmp) ?: strdupa("-");
--	      char *p_systemd_requiredby        = strtok_r(NULL, "\t", &toktmp) ?: strdupa("-");
--	const char *p_systemd_nofail            = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_ignore            = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	/* END CSTYLED */
--
--	const char *pool = dataset;
--	if ((toktmp = strchr(pool, '/')) != NULL)
--		pool = strndupa(pool, toktmp - pool);
--
--	if (p_nbmand == NULL) {
--		fprintf(stderr, PROGNAME "[%d]: %s: not enough tokens!\n",
--		    getpid(), dataset);
--		return (1);
--	}
--
--	strncpy(argv0, dataset, strlen(argv0));
--
--	/* Minimal pre-requisites to mount a ZFS dataset */
--	const char *after = "zfs-import.target";
--	const char *wants = "zfs-import.target";
--	const char *bindsto = NULL;
--	char *wantedby = NULL;
--	char *requiredby = NULL;
--	bool noauto = false;
--	bool wantedby_append = true;
--
--	/*
--	 * zfs-import.target is not needed if the pool is already imported.
--	 * This avoids a dependency loop on root-on-ZFS systems:
--	 *   systemd-random-seed.service After (via RequiresMountsFor)
--	 *   var-lib.mount After
--	 *   zfs-import.target After
--	 *   zfs-import-{cache,scan}.service After
--	 *   cryptsetup.service After
--	 *   systemd-random-seed.service
--	 */
--	if (tfind(pool, &known_pools, STRCMP)) {
--		after = "";
--		wants = "";
--	}
--
--	if (strcmp(p_systemd_after, "-") == 0)
--		p_systemd_after = NULL;
--	if (strcmp(p_systemd_before, "-") == 0)
--		p_systemd_before = NULL;
--	if (strcmp(p_systemd_requires, "-") == 0)
--		p_systemd_requires = NULL;
--	if (strcmp(p_systemd_requiresmountsfor, "-") == 0)
--		p_systemd_requiresmountsfor = NULL;
--
--
--	if (strcmp(p_encroot, "-") != 0) {
--		char *keyloadunit =
--		    systemd_escape(p_encroot, "zfs-load-key-", ".service");
--
--		if (strcmp(dataset, p_encroot) == 0) {
--			const char *keymountdep = NULL;
--			bool is_prompt = false;
--
--			regmatch_t uri_matches[3];
--			if (regexec(&uri_regex, p_keyloc,
--			    sizeof (uri_matches) / sizeof (*uri_matches),
--			    uri_matches, 0) == 0) {
--				p_keyloc[uri_matches[2].rm_eo] = '\0';
--				const char *path =
--				    &p_keyloc[uri_matches[2].rm_so];
--
--				/*
--				 * Assumes all URI keylocations need
--				 * the mount for their path;
--				 * http://, for example, wouldn't
--				 * (but it'd need network-online.target et al.)
--				 */
--				keymountdep = path;
--			} else {
--				if (strcmp(p_keyloc, "prompt") != 0)
--					fprintf(stderr, PROGNAME "[%d]: %s: "
--					    "unknown non-URI keylocation=%s\n",
--					    getpid(), dataset, p_keyloc);
--
--				is_prompt = true;
--			}
--
--
--			/* Generate the key-load .service unit */
--			FILE *keyloadunit_f = fopenat(destdir_fd, keyloadunit,
--			    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w",
--			    0644);
--			if (!keyloadunit_f) {
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "couldn't open %s under %s: %s\n",
--				    getpid(), dataset, keyloadunit, destdir,
--				    strerror(errno));
--				return (1);
--			}
--
--			fprintf(keyloadunit_f,
--			    OUTPUT_HEADER
--			    "[Unit]\n"
--			    "Description=Load ZFS key for %s\n"
--			    "SourcePath=" FSLIST "/%s\n"
--			    "Documentation=man:zfs-mount-generator(8)\n"
--			    "DefaultDependencies=no\n"
--			    "Wants=%s\n"
--			    "After=%s\n",
--			    dataset, cachefile, wants, after);
--
--			if (p_systemd_requires)
--				fprintf(keyloadunit_f,
--				    "Requires=%s\n", p_systemd_requires);
--
--			if (p_systemd_requiresmountsfor || keymountdep) {
--				fprintf(keyloadunit_f, "RequiresMountsFor=");
--				if (p_systemd_requiresmountsfor)
--					fprintf(keyloadunit_f,
--					    "%s ", p_systemd_requiresmountsfor);
--				if (keymountdep)
--					fprintf(keyloadunit_f,
--					    "'%s'", keymountdep);
--				fprintf(keyloadunit_f, "\n");
--			}
--
--			/* BEGIN CSTYLED */
--			fprintf(keyloadunit_f,
--			    "\n"
--			    "[Service]\n"
--			    "Type=oneshot\n"
--			    "RemainAfterExit=yes\n"
--			    "# This avoids a dependency loop involving systemd-journald.socket if this\n"
--			    "# dataset is a parent of the root filesystem.\n"
--			    "StandardOutput=null\n"
--			    "StandardError=null\n"
--			    "ExecStart=/bin/sh -c '"
--			        "set -eu;"
--			        "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
--			        "[ \"$$keystatus\" = \"unavailable\" ] || exit 0;",
--			    dataset);
--			if (is_prompt)
--				fprintf(keyloadunit_f,
--				    "count=0;"
--				    "while [ $$count -lt 3 ]; do "
--				        "systemd-ask-password --id=\"zfs:%s\" \"Enter passphrase for %s:\" |"
--				        "" ZFS " load-key \"%s\" && exit 0;"
--				        "count=$$((count + 1));"
--				    "done;"
--				    "exit 1",
--				    dataset, dataset, dataset);
--			else
--				fprintf(keyloadunit_f,
--				    "" ZFS " load-key \"%s\"",
--				    dataset);
--
--			fprintf(keyloadunit_f,
--				"'\n"
--				"ExecStop=/bin/sh -c '"
--				    "set -eu;"
--				    "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
--				    "[ \"$$keystatus\" = \"available\" ] || exit 0;"
--				    "" ZFS " unload-key \"%s\""
--				"'\n",
--				dataset, dataset);
--			/* END CSTYLED */
--
--			(void) fclose(keyloadunit_f);
--		}
--
--		/* Update dependencies for the mount file to want this */
--		bindsto = keyloadunit;
--		if (after[0] == '\0')
--			after = keyloadunit;
--		else if (asprintf(&toktmp, "%s %s", after, keyloadunit) != -1)
--			after = toktmp;
--		else
--			EXIT_ENOMEM();
--	}
--
--
--	/* Skip generation of the mount unit if org.openzfs.systemd:ignore=on */
--	if (strcmp(p_systemd_ignore, "-") == 0 ||
--	    strcmp(p_systemd_ignore, "off") == 0) {
--		/* ok */
--	} else if (strcmp(p_systemd_ignore, "on") == 0)
--		return (0);
--	else {
--		fprintf(stderr, PROGNAME "[%d]: %s: "
--		    "invalid org.openzfs.systemd:ignore=%s\n",
--		    getpid(), dataset, p_systemd_ignore);
--		return (1);
--	}
--
--	/* Check for canmount */
--	if (strcmp(p_canmount, "on") == 0) {
--		/* ok */
--	} else if (strcmp(p_canmount, "noauto") == 0)
--		noauto = true;
--	else if (strcmp(p_canmount, "off") == 0)
--		return (0);
--	else {
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid canmount=%s\n",
--		    getpid(), dataset, p_canmount);
--		return (1);
--	}
--
--	/* Check for legacy and blank mountpoints */
--	if (strcmp(p_mountpoint, "legacy") == 0 ||
--	    strcmp(p_mountpoint, "none") == 0)
--		return (0);
--	else if (p_mountpoint[0] != '/') {
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid mountpoint=%s\n",
--		    getpid(), dataset, p_mountpoint);
--		return (1);
--	}
--
--	/* Escape the mountpoint per systemd policy */
--	simplify_path(p_mountpoint);
--	const char *mountfile = systemd_escape_path(p_mountpoint, "", ".mount");
--	if (mountfile == NULL) {
--		fprintf(stderr,
--		    PROGNAME "[%d]: %s: abnormal simplified mountpoint: %s\n",
--		    getpid(), dataset, p_mountpoint);
--		return (1);
--	}
--
--
--	/*
--	 * Parse options, cf. lib/libzfs/libzfs_mount.c:zfs_add_options
--	 *
--	 * The longest string achievable here is
--	 * ",atime,strictatime,nodev,noexec,rw,nosuid,nomand".
--	 */
--	char opts[64] = "";
--
--	/* atime */
--	if (strcmp(p_atime, "on") == 0) {
--		/* relatime */
--		if (strcmp(p_relatime, "on") == 0)
--			strcat(opts, ",atime,relatime");
--		else if (strcmp(p_relatime, "off") == 0)
--			strcat(opts, ",atime,strictatime");
--		else
--			fprintf(stderr,
--			    PROGNAME "[%d]: %s: invalid relatime=%s\n",
--			    getpid(), dataset, p_relatime);
--	} else if (strcmp(p_atime, "off") == 0) {
--		strcat(opts, ",noatime");
--	} else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid atime=%s\n",
--		    getpid(), dataset, p_atime);
--
--	/* devices */
--	if (strcmp(p_devices, "on") == 0)
--		strcat(opts, ",dev");
--	else if (strcmp(p_devices, "off") == 0)
--		strcat(opts, ",nodev");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid devices=%s\n",
--		    getpid(), dataset, p_devices);
--
--	/* exec */
--	if (strcmp(p_exec, "on") == 0)
--		strcat(opts, ",exec");
--	else if (strcmp(p_exec, "off") == 0)
--		strcat(opts, ",noexec");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid exec=%s\n",
--		    getpid(), dataset, p_exec);
--
--	/* readonly */
--	if (strcmp(p_readonly, "on") == 0)
--		strcat(opts, ",ro");
--	else if (strcmp(p_readonly, "off") == 0)
--		strcat(opts, ",rw");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid readonly=%s\n",
--		    getpid(), dataset, p_readonly);
--
--	/* setuid */
--	if (strcmp(p_setuid, "on") == 0)
--		strcat(opts, ",suid");
--	else if (strcmp(p_setuid, "off") == 0)
--		strcat(opts, ",nosuid");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid setuid=%s\n",
--		    getpid(), dataset, p_setuid);
--
--	/* nbmand */
--	if (strcmp(p_nbmand, "on") == 0)
--		strcat(opts, ",mand");
--	else if (strcmp(p_nbmand, "off") == 0)
--		strcat(opts, ",nomand");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid nbmand=%s\n",
--		    getpid(), dataset, p_setuid);
--
--	if (strcmp(p_systemd_wantedby, "-") != 0) {
--		noauto = true;
--
--		if (strcmp(p_systemd_wantedby, "none") != 0)
--			wantedby = p_systemd_wantedby;
--	}
--
--	if (strcmp(p_systemd_requiredby, "-") != 0) {
--		noauto = true;
--
--		if (strcmp(p_systemd_requiredby, "none") != 0)
--			requiredby = p_systemd_requiredby;
--	}
--
--	/*
--	 * For datasets with canmount=on, a dependency is created for
--	 * local-fs.target by default. To avoid regressions, this dependency
--	 * is reduced to "wants" rather than "requires" when nofail!=off.
--	 * **THIS MAY CHANGE**
--	 * noauto=on disables this behavior completely.
--	 */
--	if (!noauto) {
--		if (strcmp(p_systemd_nofail, "off") == 0)
--			requiredby = strdupa("local-fs.target");
--		else {
--			wantedby = strdupa("local-fs.target");
--			wantedby_append = strcmp(p_systemd_nofail, "on") != 0;
--		}
--	}
--
--	/*
--	 * Handle existing files:
--	 * 1.	We never overwrite existing files, although we may delete
--	 * 	files if we're sure they were created by us. (see 5.)
--	 * 2.	We handle files differently based on canmount.
--	 * 	Units with canmount=on always have precedence over noauto.
--	 * 	This is enforced by the noauto_not_on_sem semaphore,
--	 * 	which is only unlocked when the last canmount=on process exits.
--	 * 	It is important to use p_canmount and not noauto here,
--	 * 	since we categorise by canmount while other properties,
--	 * 	e.g. org.openzfs.systemd:wanted-by, also modify noauto.
--	 * 3.	If no unit file exists for a noauto dataset, we create one.
--	 * 	Additionally, we use noauto_files to track the unit file names
--	 * 	(which are the systemd-escaped mountpoints) of all (exclusively)
--	 * 	noauto datasets that had a file created.
--	 * 4.	If the file to be created is found in the tracking array,
--	 * 	we do NOT create it.
--	 * 5.	If a file exists for a noauto dataset,
--	 * 	we check whether the file name is in the array.
--	 * 	If it is, we have multiple noauto datasets for the same
--	 * 	mountpoint. In such cases, we remove the file for safety.
--	 * 	We leave the file name in the tracking array to avoid
--	 * 	further noauto datasets creating a file for this path again.
--	 */
--
--	{
--		sem_t *our_sem = (strcmp(p_canmount, "on") == 0) ?
--		    &noauto_files->noauto_names_sem :
--		    &noauto_files->noauto_not_on_sem;
--		while (sem_wait(our_sem) == -1 && errno == EINTR)
--			;
--	}
--
--	struct stat stbuf;
--	bool already_exists = fstatat(destdir_fd, mountfile, &stbuf, 0) == 0;
--
--	bool is_known = false;
--	for (size_t i = 0; i < noauto_files->noauto_names_len; ++i) {
--		if (strncmp(
--		    noauto_files->noauto_names[i], mountfile, NAME_MAX) == 0) {
--			is_known = true;
--			break;
--		}
--	}
--
--	if (already_exists) {
--		if (is_known) {
--			/* If it's in $noauto_files, we must be noauto too */
--
--			/* See 5 */
--			errno = 0;
--			(void) unlinkat(destdir_fd, mountfile, 0);
--
--			/* See 2 */
--			fprintf(stderr, PROGNAME "[%d]: %s: "
--			    "removing duplicate noauto unit %s%s%s\n",
--			    getpid(), dataset, mountfile,
--			    errno ? "" : " failed: ",
--			    errno ? "" : strerror(errno));
--		} else {
--			/* Don't log for canmount=noauto */
--			if (strcmp(p_canmount, "on") == 0)
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "%s already exists. Skipping.\n",
--				    getpid(), dataset, mountfile);
--		}
--
--		/* File exists: skip current dataset */
--		if (strcmp(p_canmount, "on") == 0)
--			sem_post(&noauto_files->noauto_names_sem);
--		return (0);
--	} else {
--		if (is_known) {
--			/* See 4 */
--			if (strcmp(p_canmount, "on") == 0)
--				sem_post(&noauto_files->noauto_names_sem);
--			return (0);
--		} else if (strcmp(p_canmount, "noauto") == 0) {
--			if (noauto_files->noauto_names_len ==
--			    noauto_files->noauto_names_max)
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "noauto dataset limit (%zu) reached! "
--				    "Not tracking %s. Please report this to "
--				    "https://github.com/openzfs/zfs\n",
--				    getpid(), dataset,
--				    noauto_files->noauto_names_max, mountfile);
--			else {
--				strncpy(noauto_files->noauto_names[
--				    noauto_files->noauto_names_len],
--				    mountfile, NAME_MAX);
--				++noauto_files->noauto_names_len;
--			}
--		}
--	}
--
--
--	FILE *mountfile_f = fopenat(destdir_fd, mountfile,
--	    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w", 0644);
--	if (strcmp(p_canmount, "on") == 0)
--		sem_post(&noauto_files->noauto_names_sem);
--	if (!mountfile_f) {
--		fprintf(stderr,
--		    PROGNAME "[%d]: %s: couldn't open %s under %s: %s\n",
--		    getpid(), dataset, mountfile, destdir, strerror(errno));
--		return (1);
--	}
--
--	fprintf(mountfile_f,
--	    OUTPUT_HEADER
--	    "[Unit]\n"
--	    "SourcePath=" FSLIST "/%s\n"
--	    "Documentation=man:zfs-mount-generator(8)\n"
--	    "\n"
--	    "Before=",
--	    cachefile);
--
--	if (p_systemd_before)
--		fprintf(mountfile_f, "%s ", p_systemd_before);
--	fprintf(mountfile_f, "zfs-mount.service"); /* Ensures we don't race */
--	if (requiredby)
--		fprintf(mountfile_f, " %s", requiredby);
--	if (wantedby && wantedby_append)
--		fprintf(mountfile_f, " %s", wantedby);
--
--	fprintf(mountfile_f,
--	    "\n"
--	    "After=");
--	if (p_systemd_after)
--		fprintf(mountfile_f, "%s ", p_systemd_after);
--	fprintf(mountfile_f, "%s\n", after);
--
--	fprintf(mountfile_f, "Wants=%s\n", wants);
--
--	if (bindsto)
--		fprintf(mountfile_f, "BindsTo=%s\n", bindsto);
--	if (p_systemd_requires)
--		fprintf(mountfile_f, "Requires=%s\n", p_systemd_requires);
--	if (p_systemd_requiresmountsfor)
--		fprintf(mountfile_f,
--		    "RequiresMountsFor=%s\n", p_systemd_requiresmountsfor);
--
--	fprintf(mountfile_f,
--	    "\n"
--	    "[Mount]\n"
--	    "Where=%s\n"
--	    "What=%s\n"
--	    "Type=zfs\n"
--	    "Options=defaults%s,zfsutil\n",
--	    p_mountpoint, dataset, opts);
--
--	(void) fclose(mountfile_f);
--
--	if (!requiredby && !wantedby)
--		return (0);
--
--	/* Finally, create the appropriate dependencies */
--	char *linktgt;
--	if (asprintf(&linktgt, "../%s", mountfile) == -1)
--		EXIT_ENOMEM();
--
--	char *dependencies[][2] = {
--		{"wants", wantedby},
--		{"requires", requiredby},
--		{}
--	};
--	for (__typeof__(&*dependencies) dep = &*dependencies; **dep; ++dep) {
--		if (!(*dep)[1])
--			continue;
--
--		for (char *reqby = strtok_r((*dep)[1], " ", &toktmp);
--		    reqby;
--		    reqby = strtok_r(NULL, " ", &toktmp)) {
--			char *depdir;
--			if (asprintf(&depdir, "%s.%s", reqby, (*dep)[0]) == -1)
--				EXIT_ENOMEM();
--
--			(void) mkdirat(destdir_fd, depdir, 0755);
--			int depdir_fd = openat(destdir_fd, depdir,
--			    O_PATH | O_DIRECTORY | O_CLOEXEC);
--			if (depdir_fd < 0) {
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "couldn't open %s under %s: %s\n",
--				    getpid(), dataset, depdir, destdir,
--				    strerror(errno));
--				free(depdir);
--				continue;
--			}
--
--			if (symlinkat(linktgt, depdir_fd, mountfile) == -1)
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "couldn't symlink at "
--				    "%s under %s under %s: %s\n",
--				    getpid(), dataset, mountfile,
--				    depdir, destdir, strerror(errno));
--
--			(void) close(depdir_fd);
--			free(depdir);
--		}
--	}
--
--	return (0);
--}
--
--
--static int
--pool_enumerator(zpool_handle_t *pool, void *data __attribute__((unused)))
--{
--	int ret = 0;
--
--	/*
--	 * Pools are guaranteed-unique by the kernel,
--	 * no risk of leaking dupes here
--	 */
--	char *name = strdup(zpool_get_name(pool));
--	if (!name || !tsearch(name, &known_pools, STRCMP)) {
--		free(name);
--		ret = ENOMEM;
--	}
--
--	zpool_close(pool);
--	return (ret);
--}
--
--int
--main(int argc, char **argv)
--{
--	struct timespec time_init = {};
--	clock_gettime(CLOCK_MONOTONIC_RAW, &time_init);
--
--	{
--		int kmfd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
--		if (kmfd >= 0) {
--			(void) dup2(kmfd, STDERR_FILENO);
--			(void) close(kmfd);
--		}
--	}
--
--	uint8_t debug = 0;
--
--	argv0 = argv[0];
--	switch (argc) {
--	case 1:
--		/* Use default */
--		break;
--	case 2:
--	case 4:
--		destdir = argv[1];
--		break;
--	default:
--		fprintf(stderr,
--		    PROGNAME "[%d]: wrong argument count: %d\n",
--		    getpid(), argc - 1);
--		_exit(1);
--	}
--
--	{
--		destdir_fd = open(destdir, O_PATH | O_DIRECTORY | O_CLOEXEC);
--		if (destdir_fd < 0) {
--			fprintf(stderr, PROGNAME "[%d]: "
--			    "can't open destination directory %s: %s\n",
--			    getpid(), destdir, strerror(errno));
--			_exit(1);
--		}
--	}
--
--	DIR *fslist_dir = opendir(FSLIST);
--	if (!fslist_dir) {
--		if (errno != ENOENT)
--			fprintf(stderr,
--			    PROGNAME "[%d]: couldn't open " FSLIST ": %s\n",
--			    getpid(), strerror(errno));
--		_exit(0);
--	}
--
--	{
--		libzfs_handle_t *libzfs = libzfs_init();
--		if (libzfs) {
--			if (zpool_iter(libzfs, pool_enumerator, NULL) != 0)
--				fprintf(stderr, PROGNAME "[%d]: "
--				    "error listing pools, ignoring\n",
--				    getpid());
--			libzfs_fini(libzfs);
--		} else
--			fprintf(stderr, PROGNAME "[%d]: "
--			    "couldn't start libzfs, ignoring\n",
--			    getpid());
--	}
--
--	{
--		int regerr = regcomp(&uri_regex, URI_REGEX_S, 0);
--		if (regerr != 0) {
--			fprintf(stderr,
--			    PROGNAME "[%d]: invalid regex: %d\n",
--			    getpid(), regerr);
--			_exit(1);
--		}
--	}
--
--	{
--		/*
--		 * We could just get a gigabyte here and Not Care,
--		 * but if vm.overcommit_memory=2, then MAP_NORESERVE is ignored
--		 * and we'd try (and likely fail) to rip it out of swap
--		 */
--		noauto_files = mmap(NULL, 4 * 1024 * 1024,
--		    PROT_READ | PROT_WRITE,
--		    MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
--		if (noauto_files == MAP_FAILED) {
--			fprintf(stderr,
--			    PROGNAME "[%d]: couldn't allocate IPC region: %s\n",
--			    getpid(), strerror(errno));
--			_exit(1);
--		}
--
--		sem_init(&noauto_files->noauto_not_on_sem, true, 0);
--		sem_init(&noauto_files->noauto_names_sem, true, 1);
--		noauto_files->noauto_names_len = 0;
--		/* Works out to 16447ish, *well* enough */
--		noauto_files->noauto_names_max =
--		    (4 * 1024 * 1024 - sizeof (*noauto_files)) / NAME_MAX;
--	}
--
--	char *line = NULL;
--	size_t linelen = 0;
--	struct timespec time_start = {};
--	{
--		const char *dbgenv = getenv("ZFS_DEBUG");
--		if (dbgenv)
--			debug = atoi(dbgenv);
--		else {
--			FILE *cmdline = fopen("/proc/cmdline", "re");
--			if (cmdline != NULL) {
--				if (getline(&line, &linelen, cmdline) >= 0)
--					debug = strstr(line, "debug") ? 2 : 0;
--				(void) fclose(cmdline);
--			}
--		}
--
--		if (debug && !isatty(STDOUT_FILENO))
--			dup2(STDERR_FILENO, STDOUT_FILENO);
--	}
--
--	size_t forked_canmount_on = 0;
--	size_t forked_canmount_not_on = 0;
--	size_t canmount_on_pids_len = 128;
--	pid_t *canmount_on_pids =
--	    malloc(canmount_on_pids_len * sizeof (*canmount_on_pids));
--	if (canmount_on_pids == NULL)
--		canmount_on_pids_len = 0;
--
--	if (debug)
--		clock_gettime(CLOCK_MONOTONIC_RAW, &time_start);
--
--	ssize_t read;
--	pid_t pid;
--	struct dirent *cachent;
--	while ((cachent = readdir(fslist_dir)) != NULL) {
--		if (strcmp(cachent->d_name, ".") == 0 ||
--		    strcmp(cachent->d_name, "..") == 0)
--			continue;
--
--		FILE *cachefile = fopenat(dirfd(fslist_dir), cachent->d_name,
--		    O_RDONLY | O_CLOEXEC, "r", 0);
--		if (!cachefile) {
--			fprintf(stderr, PROGNAME "[%d]: "
--			    "couldn't open %s under " FSLIST ": %s\n",
--			    getpid(), cachent->d_name, strerror(errno));
--			continue;
--		}
--
--		while ((read = getline(&line, &linelen, cachefile)) >= 0) {
--			line[read - 1] = '\0'; /* newline */
--
--			switch (pid = fork()) {
--			case -1:
--				fprintf(stderr,
--				    PROGNAME "[%d]: couldn't fork for %s: %s\n",
--				    getpid(), line, strerror(errno));
--				break;
--			case 0: /* child */
--				_exit(line_worker(line, cachent->d_name));
--			default: { /* parent */
--				char *tmp;
--				char *dset = strtok_r(line, "\t", &tmp);
--				strtok_r(NULL, "\t", &tmp);
--				char *canmount = strtok_r(NULL, "\t", &tmp);
--				bool canmount_on =
--				    canmount && strncmp(canmount, "on", 2) == 0;
--
--				if (debug >= 2)
--					printf(PROGNAME ": forked %d, "
--					    "canmount_on=%d, dataset=%s\n",
--					    (int)pid, canmount_on, dset);
--
--				if (canmount_on &&
--				    forked_canmount_on ==
--				    canmount_on_pids_len) {
--					size_t new_len =
--					    (canmount_on_pids_len ?: 16) * 2;
--					void *new_pidlist =
--					    realloc(canmount_on_pids,
--					    new_len *
--					    sizeof (*canmount_on_pids));
--					if (!new_pidlist) {
--						fprintf(stderr,
--						    PROGNAME "[%d]: "
--						    "out of memory! "
--						    "Mount ordering may be "
--						    "affected.\n", getpid());
--						continue;
--					}
--
--					canmount_on_pids = new_pidlist;
--					canmount_on_pids_len = new_len;
--				}
--
--				if (canmount_on) {
--					canmount_on_pids[forked_canmount_on] =
--					    pid;
--					++forked_canmount_on;
--				} else
--					++forked_canmount_not_on;
--				break;
--			}
--			}
--		}
--
--		(void) fclose(cachefile);
--	}
--	free(line);
--
--	if (forked_canmount_on == 0) {
--		/* No canmount=on processes to finish, so don't deadlock here */
--		for (size_t i = 0; i < forked_canmount_not_on; ++i)
--			sem_post(&noauto_files->noauto_not_on_sem);
--	} else {
--		/* Likely a no-op, since we got these from a narrow fork loop */
--		qsort(canmount_on_pids, forked_canmount_on,
--		    sizeof (*canmount_on_pids), PID_T_CMP);
--	}
--
--	int status, ret = 0;
--	struct rusage usage;
--	size_t forked_canmount_on_max = forked_canmount_on;
--	while ((pid = wait4(-1, &status, 0, &usage)) != -1) {
--		ret |= WEXITSTATUS(status) | WTERMSIG(status);
--
--		if (forked_canmount_on != 0) {
--			if (bsearch(&pid, canmount_on_pids,
--			    forked_canmount_on_max, sizeof (*canmount_on_pids),
--			    PID_T_CMP))
--				--forked_canmount_on;
--
--			if (forked_canmount_on == 0) {
--				/*
--				 * All canmount=on processes have finished,
--				 * let all the lower-priority ones finish now
--				 */
--				for (size_t i = 0;
--				    i < forked_canmount_not_on; ++i)
--					sem_post(
--					    &noauto_files->noauto_not_on_sem);
--			}
--		}
--
--		if (debug >= 2)
--			printf(PROGNAME ": %d done, user=%llu.%06us, "
--			    "system=%llu.%06us, maxrss=%ldB, ex=0x%x\n",
--			    (int)pid,
--			    (unsigned long long) usage.ru_utime.tv_sec,
--			    (unsigned int) usage.ru_utime.tv_usec,
--			    (unsigned long long) usage.ru_stime.tv_sec,
--			    (unsigned int) usage.ru_stime.tv_usec,
--			    usage.ru_maxrss * 1024, status);
--	}
--
--	if (debug) {
--		struct timespec time_end = {};
--		clock_gettime(CLOCK_MONOTONIC_RAW, &time_end);
--
--		getrusage(RUSAGE_SELF, &usage);
--		printf(
--		    "\n"
--		    PROGNAME ": self    : "
--		    "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
--		    (unsigned long long) usage.ru_utime.tv_sec,
--		    (unsigned int) usage.ru_utime.tv_usec,
--		    (unsigned long long) usage.ru_stime.tv_sec,
--		    (unsigned int) usage.ru_stime.tv_usec,
--		    usage.ru_maxrss * 1024);
--
--		getrusage(RUSAGE_CHILDREN, &usage);
--		printf(PROGNAME ": children: "
--		    "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
--		    (unsigned long long) usage.ru_utime.tv_sec,
--		    (unsigned int) usage.ru_utime.tv_usec,
--		    (unsigned long long) usage.ru_stime.tv_sec,
--		    (unsigned int) usage.ru_stime.tv_usec,
--		    usage.ru_maxrss * 1024);
--
--		if (time_start.tv_nsec > time_end.tv_nsec) {
--			time_end.tv_nsec =
--			    1000000000 + time_end.tv_nsec - time_start.tv_nsec;
--			time_end.tv_sec -= 1;
--		} else
--			time_end.tv_nsec -= time_start.tv_nsec;
--		time_end.tv_sec -= time_start.tv_sec;
--
--		if (time_init.tv_nsec > time_start.tv_nsec) {
--			time_start.tv_nsec =
--			    1000000000 + time_start.tv_nsec - time_init.tv_nsec;
--			time_start.tv_sec -= 1;
--		} else
--			time_start.tv_nsec -= time_init.tv_nsec;
--		time_start.tv_sec -= time_init.tv_sec;
--
--		time_init.tv_nsec = time_start.tv_nsec + time_end.tv_nsec;
--		time_init.tv_sec =
--		    time_start.tv_sec + time_end.tv_sec +
--		    time_init.tv_nsec / 1000000000;
--		time_init.tv_nsec %= 1000000000;
--
--		printf(PROGNAME ": wall    : "
--		    "total=%llu.%09llus = "
--		    "init=%llu.%09llus + real=%llu.%09llus\n",
--		    (unsigned long long) time_init.tv_sec,
--		    (unsigned long long) time_init.tv_nsec,
--		    (unsigned long long) time_start.tv_sec,
--		    (unsigned long long) time_start.tv_nsec,
--		    (unsigned long long) time_end.tv_sec,
--		    (unsigned long long) time_end.tv_nsec);
--	}
--
--	_exit(ret);
--}
-diff --git a/etc/systemd/system-generators/zfs-mount-generator.in b/etc/systemd/system-generators/zfs-mount-generator.in
-new file mode 100755
-index 0000000000..c276fbbce5
+Index: zfs-linux/etc/systemd/system-generators/zfs-mount-generator.in
+===================================================================
 --- /dev/null
-+++ b/etc/systemd/system-generators/zfs-mount-generator.in
++++ zfs-linux/etc/systemd/system-generators/zfs-mount-generator.in
 @@ -0,0 +1,474 @@
 +#!/bin/sh
 +
@@ -1629,6 +509,352 @@ index 0000000000..c276fbbce5
 +    done
 +  )
 +done
--- 
-2.32.0
-
+Index: zfs-linux/man/man8/zfs-mount-generator.8.in
+===================================================================
+--- zfs-linux.orig/man/man8/zfs-mount-generator.8.in
++++ zfs-linux/man/man8/zfs-mount-generator.8.in
+@@ -21,98 +21,31 @@
+ .\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ .\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ .\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+-.\"
+-.Dd May 31, 2021
+-.Dt ZFS-MOUNT-GENERATOR 8
+-.Os
+-.
+-.Sh NAME
+-.Nm zfs-mount-generator
+-.Nd generate systemd mount units for ZFS filesystems
+-.Sh SYNOPSIS
+-.Pa @systemdgeneratordir@/zfs-mount-generator
+-.
+-.Sh DESCRIPTION
+-.Nm
+-is a
+-.Xr systemd.generator 7
+-that generates native
+-.Xr systemd.mount 5
+-units for configured ZFS datasets.
+-.
+-.Ss Properties
+-.Bl -tag -compact -width "org.openzfs.systemd:required-by=unit[ unit]…"
+-.It Sy mountpoint Ns =
+-.No Skipped if Sy legacy No or Sy none .
+-.
+-.It Sy canmount Ns =
+-.No Skipped if Sy off .
+-.No Skipped if only Sy noauto
+-datasets exist for a given mountpoint and there's more than one.
+-.No Datasets with Sy yes No take precedence over ones with Sy noauto No for the same mountpoint .
+-.No Sets logical Em noauto No flag if Sy noauto .
+-Encryption roots always generate
+-.Sy zfs-load-key@ Ns Ar root Ns Sy .service ,
+-even if
+-.Sy off .
+-.
+-.It Sy atime Ns = , Sy relatime Ns = , Sy devices Ns = , Sy exec Ns = , Sy readonly Ns = , Sy setuid Ns = , Sy nbmand Ns =
+-Used to generate mount options equivalent to
+-.Nm zfs Cm mount .
+-.
+-.It Sy encroot Ns = , Sy keylocation Ns =
+-If the dataset is an encryption root, its mount unit will bind to
+-.Sy zfs-load-key@ Ns Ar root Ns Sy .service ,
+-with additional dependencies as follows:
+-.Bl -tag -compact -offset Ds -width "keylocation=https://URL (et al.)"
+-.It Sy keylocation Ns = Ns Sy prompt
+-None, uses
+-.Xr systemd-ask-password 1
+-.It Sy keylocation Ns = Ns Sy https:// Ns Ar URL Pq et al.\&
+-.Sy Wants Ns = , Sy After Ns = : Pa network-online.target
+-.It Sy keylocation Ns = Ns Sy file:// Ns < Ns Ar path Ns >
+-.Sy RequiresMountsFor Ns = Ns Ar path
+-.El
+-.
+-The service also uses the same
+-.Sy Wants Ns = ,
+-.Sy After Ns = ,
+-.Sy Requires Ns = , No and
+-.Sy RequiresMountsFor Ns = ,
+-as the mount unit.
+-.
+-.It Sy org.openzfs.systemd:requires Ns = Ns Pa path Ns Oo " " Ns Pa path Oc Ns …
+-.No Sets Sy Requires Ns = for the mount- and key-loading unit.
+-.
+-.It Sy org.openzfs.systemd:requires-mounts-for Ns = Ns Pa path Ns Oo " " Ns Pa path Oc Ns …
+-.No Sets Sy RequiresMountsFor Ns = for the mount- and key-loading unit.
+-.
+-.It Sy org.openzfs.systemd:before Ns = Ns Pa unit Ns Oo " " Ns Pa unit Oc Ns …
+-.No Sets Sy Before Ns = for the mount unit.
+-.
+-.It Sy org.openzfs.systemd:after Ns = Ns Pa unit Ns Oo " " Ns Pa unit Oc Ns …
+-.No Sets Sy After Ns = for the mount unit.
+-.
+-.It Sy org.openzfs.systemd:wanted-by Ns = Ns Pa unit Ns Oo " " Ns Pa unit Oc Ns …
+-.No Sets logical Em noauto No flag (see below) .
+-.No If not Sy none , No sets Sy WantedBy Ns = for the mount unit.
+-.It Sy org.openzfs.systemd:required-by Ns = Ns Pa unit Ns Oo " " Ns Pa unit Oc Ns …
+-.No Sets logical Em noauto No flag (see below) .
+-.No If not Sy none , No sets Sy RequiredBy Ns = for the mount unit.
+-.
+-.It Sy org.openzfs.systemd:nofail Ns = Ns (unset) Ns | Ns Sy on Ns | Ns Sy off
+-Waxes or wanes strength of default reverse dependencies of the mount unit, see
+-below.
+-.
+-.It Sy org.openzfs.systemd:ignore Ns = Ns Sy on Ns | Ns Sy off
+-.No Skip if Sy on .
+-.No Defaults to Sy off .
+-.El
+-.
+-.Ss Unit Ordering And Dependencies
+-Additionally, unless the pool the dataset resides on
+-is imported at generation time, both units gain
+-.Sy Wants Ns = Ns Pa zfs-import.target
++
++.TH ZFS-MOUNT-GENERATOR 8 "Aug 24, 2020" OpenZFS
++
++.SH "NAME"
++zfs\-mount\-generator \- generates systemd mount units for ZFS
++.SH SYNOPSIS
++.B @systemdgeneratordir@/zfs\-mount\-generator
++.sp
++.SH DESCRIPTION
++zfs\-mount\-generator implements the \fBGenerators Specification\fP
++of
++.BR systemd (1),
++and is called during early boot to generate
++.BR systemd.mount (5)
++units for automatically mounted datasets. Mount ordering and dependencies
++are created for all tracked pools (see below).
++
++.SS ENCRYPTION KEYS
++If the dataset is an encryption root, a service that loads the associated key (either from file or through a
++.BR systemd\-ask\-password (1)
++prompt) will be created. This service
++. BR RequiresMountsFor
++the path of the key (if file-based) and also copies the mount unit's
++.BR After ,
++.BR Before
+ and
+ .Sy After Ns = Ns Pa zfs-import.target .
+ .Pp
+@@ -132,51 +65,173 @@ of strength
+ .
+ .Ss Cache File
+ Because ZFS pools may not be available very early in the boot process,
+-information on ZFS mountpoints must be stored separately.
+-The output of
+-.Dl Nm zfs Cm list Fl Ho Ar name , Ns Aq every property above in order
+-for datasets that should be mounted by systemd should be kept at
+-.Pa @sysconfdir@/zfs/zfs-list.cache/ Ns Ar poolname ,
+-and, if writeable, will be kept synchronized for the entire pool by the
+-.Pa history_event-zfs-list-cacher.sh
+-ZEDLET, if enabled
+-.Pq see Xr zed 8 .
+-.
+-.Sh ENVIRONMENT
+-If the
+-.Sy ZFS_DEBUG
+-environment variable is nonzero
+-.Pq or unset and Pa /proc/cmdline No contains Qq Sy debug ,
+-print summary accounting information at the end.
+-.
+-.Sh EXAMPLES
++information on ZFS mountpoints must be stored separately. The output of the command
++.PP
++.RS 4
++zfs list -H -o name,mountpoint,canmount,atime,relatime,devices,exec,readonly,setuid,nbmand,encroot,keylocation,org.openzfs.systemd:requires,org.openzfs.systemd:requires-mounts-for,org.openzfs.systemd:before,org.openzfs.systemd:after,org.openzfs.systemd:wanted-by,org.openzfs.systemd:required-by,org.openzfs.systemd:nofail,org.openzfs.systemd:ignore
++
++.RE
++.PP
++for datasets that should be mounted by systemd, should be kept
++separate from the pool, at
++.PP
++.RS 4
++.RI @sysconfdir@/zfs/zfs-list.cache/ POOLNAME
++.
++.RE
++.PP
++The cache file, if writeable, will be kept synchronized with the pool
++state by the ZEDLET
++.PP
++.RS 4
++history_event-zfs-list-cacher.sh .
++.RE
++.PP
++.sp
++.SS PROPERTIES
++The behavior of the generator script can be influenced by the following dataset properties:
++.sp
++.TP 4
++.BR canmount = on | off | noauto
++If a dataset has
++.BR mountpoint
++set and
++.BR canmount
++is not
++.BR off ,
++a mount unit will be generated.
++Additionally, if
++.BR canmount
++is
++.BR on ,
++.BR local-fs.target
++will gain a dependency on the mount unit.
++
++This behavior is equal to the
++.BR auto
++and
++.BR noauto
++legacy mount options, see
++.BR systemd.mount (5).
++
++Encryption roots always generate a key-load service, even for
++.BR canmount=off .
++.TP 4
++.BR org.openzfs.systemd:requires\-mounts\-for = \fIpath\fR...
++Space\-separated list of mountpoints to require to be mounted for this mount unit
++.TP 4
++.BR org.openzfs.systemd:before = \fIunit\fR...
++The mount unit and associated key\-load service will be ordered before this space\-separated list of units.
++.TP 4
++.BR org.openzfs.systemd:after = \fIunit\fR...
++The mount unit and associated key\-load service will be ordered after this space\-separated list of units.
++.TP 4
++.BR org.openzfs.systemd:wanted\-by = \fIunit\fR...
++Space-separated list of units that will gain a
++.BR Wants
++dependency on this mount unit.
++Setting this property implies
++.BR noauto .
++.TP 4
++.BR org.openzfs.systemd:required\-by = \fIunit\fR...
++Space-separated list of units that will gain a
++.BR Requires
++dependency on this mount unit.
++Setting this property implies
++.BR noauto .
++.TP 4
++.BR org.openzfs.systemd:nofail = unset | on | off
++Toggles between a
++.BR Wants
++and
++.BR Requires
++type of dependency between the mount unit and
++.BR local-fs.target ,
++if
++.BR noauto
++isn't set or implied.
++
++.BR on :
++Mount will be
++.BR WantedBy
++local-fs.target
++
++.BR off :
++Mount will be
++.BR Before
++and
++.BR RequiredBy
++local-fs.target
++
++.BR unset :
++Mount will be
++.BR Before
++and
++.BR WantedBy
++local-fs.target
++.TP 4
++.BR org.openzfs.systemd:ignore = on | off
++If set to
++.BR on ,
++do not generate a mount unit for this dataset.
++
++See also
++.BR systemd.mount (5)
++
++.PP
++.SH EXAMPLE
+ To begin, enable tracking for the pool:
+-.Dl # Nm touch Pa @sysconfdir@/zfs/zfs-list.cache/ Ns Ar poolname
+-Then enable the tracking ZEDLET:
+-.Dl # Nm ln Fl s Pa @zfsexecdir@/zed.d/history_event-zfs-list-cacher.sh @sysconfdir@/zfs/zed.d
+-.Dl # Nm systemctl Cm enable Pa zfs-zed.service
+-.Dl # Nm systemctl Cm restart Pa zfs-zed.service
+-.Pp
+-If no history event is in the queue,
+-inject one to ensure the ZEDLET runs to refresh the cache file
+-by setting a monitored property somewhere on the pool:
+-.Dl # Nm zfs Cm set Sy relatime Ns = Ns Sy off Ar poolname/dset
+-.Dl # Nm zfs Cm inherit Sy relatime Ar poolname/dset
+-.Pp
+-To test the generator output:
+-.Dl $ Nm mkdir Pa /tmp/zfs-mount-generator
+-.Dl $ Nm @systemdgeneratordir@/zfs-mount-generator Pa /tmp/zfs-mount-generator
+-.
+-If the generated units are satisfactory, instruct
+-.Nm systemd
+-to re-run all generators:
+-.Dl # Nm systemctl daemon-reload
+-.
+-.Sh SEE ALSO
+-.Xr systemd.mount 5 ,
+-.Xr systemd.target 5 ,
+-.Xr zfs 5 ,
+-.Xr systemd.generator 7 ,
+-.Xr systemd.special 7 ,
+-.Xr zed 8 ,
+-.Xr zpool-events 8
++.PP
++.RS 4
++touch
++.RI @sysconfdir@/zfs/zfs-list.cache/ POOLNAME
++.RE
++.PP
++Then, enable the tracking ZEDLET:
++.PP
++.RS 4
++ln -s "@zfsexecdir@/zed.d/history_event-zfs-list-cacher.sh" "@sysconfdir@/zfs/zed.d"
++
++systemctl enable zfs-zed.service
++
++systemctl restart zfs-zed.service
++.RE
++.PP
++Force the running of the ZEDLET by setting a monitored property, e.g.
++.BR canmount ,
++for at least one dataset in the pool:
++.PP
++.RS 4
++zfs set canmount=on
++.I DATASET
++.RE
++.PP
++This forces an update to the stale cache file.
++
++To test the generator output, run
++.PP
++.RS 4
++@systemdgeneratordir@/zfs-mount-generator /tmp/zfs-mount-generator . .
++.RE
++.PP
++This will generate units and dependencies in
++.I /tmp/zfs-mount-generator
++for you to inspect them. The second and third argument are ignored.
++
++If you're satisfied with the generated units, instruct systemd to re-run all generators:
++.PP
++.RS 4
++systemctl daemon-reload
++.RE
++.PP
++
++.sp
++.SH SEE ALSO
++.BR zfs (5)
++.BR zfs-events (5)
++.BR zed (8)
++.BR zpool (5)
++.BR systemd (1)
++.BR systemd.target (5)
++.BR systemd.special (7)
++.BR systemd.mount (7)
diff -pruN 2.3.4-1/debian/patches/ubuntu/0003-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0003-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch
--- 2.3.4-1/debian/patches/ubuntu/0003-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch	2024-09-05 02:37:33.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0003-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,1634 +0,0 @@
-From 7b5a6894d4b083e89c217244aee53e35aa8e4936 Mon Sep 17 00:00:00 2001
-From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
-Date: Mon, 17 Jan 2022 14:00:42 +0000
-Subject: [PATCH 3/3] Revert "etc/systemd/zfs-mount-generator: rewrite in C"
-
-This reverts commit 0382362ce06a5514a97bbbf11dfe55e7e408898a.
-
-This is to continue support for ubuntu specific zsys patch that
-currently relies on the shell based implementation of the
-generator. See https://bugs.launchpad.net/bugs/1958142
-
-Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
----
- Makefile.am                                   |    2 +-
- etc/systemd/system-generators/Makefile.am     |   14 +-
- .../system-generators/zfs-mount-generator.c   | 1089 -----------------
- .../system-generators/zfs-mount-generator.in  |  474 +++++++
- 4 files changed, 478 insertions(+), 1101 deletions(-)
- delete mode 100644 etc/systemd/system-generators/zfs-mount-generator.c
- create mode 100755 etc/systemd/system-generators/zfs-mount-generator.in
-
-diff --git a/Makefile.am b/Makefile.am
-index 34fe16ce41..d4e69a749c 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -8,7 +8,7 @@ SUBDIRS += rpm
- endif
- 
- if CONFIG_USER
--SUBDIRS += man scripts lib tests cmd etc contrib
-+SUBDIRS += etc man scripts lib tests cmd contrib
- if BUILD_LINUX
- SUBDIRS += udev
- endif
-diff --git a/etc/systemd/system-generators/Makefile.am b/etc/systemd/system-generators/Makefile.am
-index e5920bf392..fee88dad8c 100644
---- a/etc/systemd/system-generators/Makefile.am
-+++ b/etc/systemd/system-generators/Makefile.am
-@@ -1,14 +1,6 @@
--include $(top_srcdir)/config/Rules.am
-+include $(top_srcdir)/config/Substfiles.am
- 
--systemdgenerator_PROGRAMS = \
-+systemdgenerator_SCRIPTS = \
- 	zfs-mount-generator
- 
--zfs_mount_generator_SOURCES = \
--	zfs-mount-generator.c
--
--zfs_mount_generator_LDADD = \
--	$(abs_top_builddir)/lib/libzfs/libzfs.la
--
--zfs_mount_generator_LDFLAGS = -pthread
--
--include $(top_srcdir)/config/CppCheck.am
-+SUBSTFILES += $(systemdgenerator_SCRIPTS)
-diff --git a/etc/systemd/system-generators/zfs-mount-generator.c b/etc/systemd/system-generators/zfs-mount-generator.c
-deleted file mode 100644
-index 8deeed9df0..0000000000
---- a/etc/systemd/system-generators/zfs-mount-generator.c
-+++ /dev/null
-@@ -1,1089 +0,0 @@
--/*
-- * Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
-- * Copyright (c) 2020 InsanePrawn <insane.prawny@gmail.com>
-- *
-- * Permission is hereby granted, free of charge, to any person obtaining
-- * a copy of this software and associated documentation files (the
-- * "Software"), to deal in the Software without restriction, including
-- * without limitation the rights to use, copy, modify, merge, publish,
-- * distribute, sublicense, and/or sell copies of the Software, and to
-- * permit persons to whom the Software is furnished to do so, subject to
-- * the following conditions:
-- *
-- * The above copyright notice and this permission notice shall be
-- * included in all copies or substantial portions of the Software.
-- *
-- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- */
--
--
--#include <sys/resource.h>
--#include <sys/types.h>
--#include <sys/time.h>
--#include <sys/stat.h>
--#include <sys/wait.h>
--#include <sys/mman.h>
--#include <semaphore.h>
--#include <stdbool.h>
--#include <unistd.h>
--#include <fcntl.h>
--#include <stdio.h>
--#include <time.h>
--#include <regex.h>
--#include <search.h>
--#include <dirent.h>
--#include <string.h>
--#include <stdlib.h>
--#include <limits.h>
--#include <errno.h>
--#include <libzfs.h>
--
--#define	STRCMP ((int(*)(const void *, const void *))&strcmp)
--#define	PID_T_CMP ((int(*)(const void *, const void *))&pid_t_cmp)
--
--static int
--pid_t_cmp(const pid_t *lhs, const pid_t *rhs)
--{
--	/*
--	 * This is always valid, quoth sys_types.h(7posix):
--	 * > blksize_t, pid_t, and ssize_t shall be signed integer types.
--	 */
--	return (*lhs - *rhs);
--}
--
--#define	EXIT_ENOMEM() \
--	do { \
--		fprintf(stderr, PROGNAME "[%d]: " \
--		    "not enough memory (L%d)!\n", getpid(), __LINE__); \
--		_exit(1); \
--	} while (0)
--
--
--#define	PROGNAME "zfs-mount-generator"
--#define	FSLIST SYSCONFDIR "/zfs/zfs-list.cache"
--#define	ZFS SBINDIR "/zfs"
--
--#define	OUTPUT_HEADER \
--	"# Automatically generated by " PROGNAME "\n" \
--	"\n"
--
--/*
-- * Starts like the one in libzfs_util.c but also matches "//"
-- * and captures until the end, since we actually use it for path extraxion
-- */
--#define	URI_REGEX_S "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):\\/\\/\\(.*\\)$"
--static regex_t uri_regex;
--
--static char *argv0;
--
--static const char *destdir = "/tmp";
--static int destdir_fd = -1;
--
--static void *known_pools = NULL; /* tsearch() of C strings */
--static struct {
--	sem_t noauto_not_on_sem;
--
--	sem_t noauto_names_sem;
--	size_t noauto_names_len;
--	size_t noauto_names_max;
--	char noauto_names[][NAME_MAX];
--} *noauto_files;
--
--
--static char *
--systemd_escape(const char *input, const char *prepend, const char *append)
--{
--	size_t len = strlen(input);
--	size_t applen = strlen(append);
--	size_t prelen = strlen(prepend);
--	char *ret = malloc(4 * len + prelen + applen + 1);
--	if (!ret)
--		EXIT_ENOMEM();
--
--	memcpy(ret, prepend, prelen);
--	char *out = ret + prelen;
--
--	const char *cur = input;
--	if (*cur == '.') {
--		memcpy(out, "\\x2e", 4);
--		out += 4;
--		++cur;
--	}
--	for (; *cur; ++cur) {
--		if (*cur == '/')
--			*(out++) = '-';
--		else if (strchr(
--		    "0123456789"
--		    "abcdefghijklmnopqrstuvwxyz"
--		    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
--		    ":_.", *cur))
--			*(out++) = *cur;
--		else {
--			sprintf(out, "\\x%02x", (int)*cur);
--			out += 4;
--		}
--	}
--
--	memcpy(out, append, applen + 1);
--	return (ret);
--}
--
--static void
--simplify_path(char *path)
--{
--	char *out = path;
--	for (char *cur = path; *cur; ++cur) {
--		if (*cur == '/') {
--			while (*(cur + 1) == '/')
--				++cur;
--			*(out++) = '/';
--		} else
--			*(out++) = *cur;
--	}
--
--	*(out++) = '\0';
--}
--
--static bool
--strendswith(const char *what, const char *suff)
--{
--	size_t what_l = strlen(what);
--	size_t suff_l = strlen(suff);
--
--	return ((what_l >= suff_l) &&
--	    (strcmp(what + what_l - suff_l, suff) == 0));
--}
--
--/* Assumes already-simplified path, doesn't modify input */
--static char *
--systemd_escape_path(char *input, const char *prepend, const char *append)
--{
--	if (strcmp(input, "/") == 0) {
--		char *ret;
--		if (asprintf(&ret, "%s-%s", prepend, append) == -1)
--			EXIT_ENOMEM();
--		return (ret);
--	} else {
--		/*
--		 * path_is_normalized() (flattened for absolute paths here),
--		 * required for proper escaping
--		 */
--		if (strstr(input, "/./") || strstr(input, "/../") ||
--		    strendswith(input, "/.") || strendswith(input, "/.."))
--			return (NULL);
--
--
--		if (input[0] == '/')
--			++input;
--
--		char *back = &input[strlen(input) - 1];
--		bool deslash = *back == '/';
--		if (deslash)
--			*back = '\0';
--
--		char *ret = systemd_escape(input, prepend, append);
--
--		if (deslash)
--			*back = '/';
--		return (ret);
--	}
--}
--
--static FILE *
--fopenat(int dirfd, const char *pathname, int flags,
--    const char *stream_mode, mode_t mode)
--{
--	int fd = openat(dirfd, pathname, flags, mode);
--	if (fd < 0)
--		return (NULL);
--
--	return (fdopen(fd, stream_mode));
--}
--
--static int
--line_worker(char *line, const char *cachefile)
--{
--	char *toktmp;
--	/* BEGIN CSTYLED */
--	const char *dataset                     = strtok_r(line, "\t", &toktmp);
--	      char *p_mountpoint                = strtok_r(NULL, "\t", &toktmp);
--	const char *p_canmount                  = strtok_r(NULL, "\t", &toktmp);
--	const char *p_atime                     = strtok_r(NULL, "\t", &toktmp);
--	const char *p_relatime                  = strtok_r(NULL, "\t", &toktmp);
--	const char *p_devices                   = strtok_r(NULL, "\t", &toktmp);
--	const char *p_exec                      = strtok_r(NULL, "\t", &toktmp);
--	const char *p_readonly                  = strtok_r(NULL, "\t", &toktmp);
--	const char *p_setuid                    = strtok_r(NULL, "\t", &toktmp);
--	const char *p_nbmand                    = strtok_r(NULL, "\t", &toktmp);
--	const char *p_encroot                   = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	      char *p_keyloc                    = strtok_r(NULL, "\t", &toktmp) ?: strdupa("none");
--	const char *p_systemd_requires          = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_requiresmountsfor = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_before            = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_after             = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	      char *p_systemd_wantedby          = strtok_r(NULL, "\t", &toktmp) ?: strdupa("-");
--	      char *p_systemd_requiredby        = strtok_r(NULL, "\t", &toktmp) ?: strdupa("-");
--	const char *p_systemd_nofail            = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	const char *p_systemd_ignore            = strtok_r(NULL, "\t", &toktmp) ?: "-";
--	/* END CSTYLED */
--
--	const char *pool = dataset;
--	if ((toktmp = strchr(pool, '/')) != NULL)
--		pool = strndupa(pool, toktmp - pool);
--
--	if (p_nbmand == NULL) {
--		fprintf(stderr, PROGNAME "[%d]: %s: not enough tokens!\n",
--		    getpid(), dataset);
--		return (1);
--	}
--
--	strncpy(argv0, dataset, strlen(argv0));
--
--	/* Minimal pre-requisites to mount a ZFS dataset */
--	const char *after = "zfs-import.target";
--	const char *wants = "zfs-import.target";
--	const char *bindsto = NULL;
--	char *wantedby = NULL;
--	char *requiredby = NULL;
--	bool noauto = false;
--	bool wantedby_append = true;
--
--	/*
--	 * zfs-import.target is not needed if the pool is already imported.
--	 * This avoids a dependency loop on root-on-ZFS systems:
--	 *   systemd-random-seed.service After (via RequiresMountsFor)
--	 *   var-lib.mount After
--	 *   zfs-import.target After
--	 *   zfs-import-{cache,scan}.service After
--	 *   cryptsetup.service After
--	 *   systemd-random-seed.service
--	 */
--	if (tfind(pool, &known_pools, STRCMP)) {
--		after = "";
--		wants = "";
--	}
--
--	if (strcmp(p_systemd_after, "-") == 0)
--		p_systemd_after = NULL;
--	if (strcmp(p_systemd_before, "-") == 0)
--		p_systemd_before = NULL;
--	if (strcmp(p_systemd_requires, "-") == 0)
--		p_systemd_requires = NULL;
--	if (strcmp(p_systemd_requiresmountsfor, "-") == 0)
--		p_systemd_requiresmountsfor = NULL;
--
--
--	if (strcmp(p_encroot, "-") != 0) {
--		char *keyloadunit =
--		    systemd_escape(p_encroot, "zfs-load-key-", ".service");
--
--		if (strcmp(dataset, p_encroot) == 0) {
--			const char *keymountdep = NULL;
--			bool is_prompt = false;
--
--			regmatch_t uri_matches[3];
--			if (regexec(&uri_regex, p_keyloc,
--			    sizeof (uri_matches) / sizeof (*uri_matches),
--			    uri_matches, 0) == 0) {
--				p_keyloc[uri_matches[2].rm_eo] = '\0';
--				const char *path =
--				    &p_keyloc[uri_matches[2].rm_so];
--
--				/*
--				 * Assumes all URI keylocations need
--				 * the mount for their path;
--				 * http://, for example, wouldn't
--				 * (but it'd need network-online.target et al.)
--				 */
--				keymountdep = path;
--			} else {
--				if (strcmp(p_keyloc, "prompt") != 0)
--					fprintf(stderr, PROGNAME "[%d]: %s: "
--					    "unknown non-URI keylocation=%s\n",
--					    getpid(), dataset, p_keyloc);
--
--				is_prompt = true;
--			}
--
--
--			/* Generate the key-load .service unit */
--			FILE *keyloadunit_f = fopenat(destdir_fd, keyloadunit,
--			    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w",
--			    0644);
--			if (!keyloadunit_f) {
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "couldn't open %s under %s: %s\n",
--				    getpid(), dataset, keyloadunit, destdir,
--				    strerror(errno));
--				return (1);
--			}
--
--			fprintf(keyloadunit_f,
--			    OUTPUT_HEADER
--			    "[Unit]\n"
--			    "Description=Load ZFS key for %s\n"
--			    "SourcePath=" FSLIST "/%s\n"
--			    "Documentation=man:zfs-mount-generator(8)\n"
--			    "DefaultDependencies=no\n"
--			    "Wants=%s\n"
--			    "After=%s\n",
--			    dataset, cachefile, wants, after);
--
--			if (p_systemd_requires)
--				fprintf(keyloadunit_f,
--				    "Requires=%s\n", p_systemd_requires);
--
--			if (p_systemd_requiresmountsfor || keymountdep) {
--				fprintf(keyloadunit_f, "RequiresMountsFor=");
--				if (p_systemd_requiresmountsfor)
--					fprintf(keyloadunit_f,
--					    "%s ", p_systemd_requiresmountsfor);
--				if (keymountdep)
--					fprintf(keyloadunit_f,
--					    "'%s'", keymountdep);
--				fprintf(keyloadunit_f, "\n");
--			}
--
--			/* BEGIN CSTYLED */
--			fprintf(keyloadunit_f,
--			    "\n"
--			    "[Service]\n"
--			    "Type=oneshot\n"
--			    "RemainAfterExit=yes\n"
--			    "# This avoids a dependency loop involving systemd-journald.socket if this\n"
--			    "# dataset is a parent of the root filesystem.\n"
--			    "StandardOutput=null\n"
--			    "StandardError=null\n"
--			    "ExecStart=/bin/sh -c '"
--			        "set -eu;"
--			        "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
--			        "[ \"$$keystatus\" = \"unavailable\" ] || exit 0;",
--			    dataset);
--			if (is_prompt)
--				fprintf(keyloadunit_f,
--				    "count=0;"
--				    "while [ $$count -lt 3 ]; do "
--				        "systemd-ask-password --id=\"zfs:%s\" \"Enter passphrase for %s:\" |"
--				        "" ZFS " load-key \"%s\" && exit 0;"
--				        "count=$$((count + 1));"
--				    "done;"
--				    "exit 1",
--				    dataset, dataset, dataset);
--			else
--				fprintf(keyloadunit_f,
--				    "" ZFS " load-key \"%s\"",
--				    dataset);
--
--			fprintf(keyloadunit_f,
--				"'\n"
--				"ExecStop=/bin/sh -c '"
--				    "set -eu;"
--				    "keystatus=\"$$(" ZFS " get -H -o value keystatus \"%s\")\";"
--				    "[ \"$$keystatus\" = \"available\" ] || exit 0;"
--				    "" ZFS " unload-key \"%s\""
--				"'\n",
--				dataset, dataset);
--			/* END CSTYLED */
--
--			(void) fclose(keyloadunit_f);
--		}
--
--		/* Update dependencies for the mount file to want this */
--		bindsto = keyloadunit;
--		if (after[0] == '\0')
--			after = keyloadunit;
--		else if (asprintf(&toktmp, "%s %s", after, keyloadunit) != -1)
--			after = toktmp;
--		else
--			EXIT_ENOMEM();
--	}
--
--
--	/* Skip generation of the mount unit if org.openzfs.systemd:ignore=on */
--	if (strcmp(p_systemd_ignore, "-") == 0 ||
--	    strcmp(p_systemd_ignore, "off") == 0) {
--		/* ok */
--	} else if (strcmp(p_systemd_ignore, "on") == 0)
--		return (0);
--	else {
--		fprintf(stderr, PROGNAME "[%d]: %s: "
--		    "invalid org.openzfs.systemd:ignore=%s\n",
--		    getpid(), dataset, p_systemd_ignore);
--		return (1);
--	}
--
--	/* Check for canmount */
--	if (strcmp(p_canmount, "on") == 0) {
--		/* ok */
--	} else if (strcmp(p_canmount, "noauto") == 0)
--		noauto = true;
--	else if (strcmp(p_canmount, "off") == 0)
--		return (0);
--	else {
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid canmount=%s\n",
--		    getpid(), dataset, p_canmount);
--		return (1);
--	}
--
--	/* Check for legacy and blank mountpoints */
--	if (strcmp(p_mountpoint, "legacy") == 0 ||
--	    strcmp(p_mountpoint, "none") == 0)
--		return (0);
--	else if (p_mountpoint[0] != '/') {
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid mountpoint=%s\n",
--		    getpid(), dataset, p_mountpoint);
--		return (1);
--	}
--
--	/* Escape the mountpoint per systemd policy */
--	simplify_path(p_mountpoint);
--	const char *mountfile = systemd_escape_path(p_mountpoint, "", ".mount");
--	if (mountfile == NULL) {
--		fprintf(stderr,
--		    PROGNAME "[%d]: %s: abnormal simplified mountpoint: %s\n",
--		    getpid(), dataset, p_mountpoint);
--		return (1);
--	}
--
--
--	/*
--	 * Parse options, cf. lib/libzfs/libzfs_mount.c:zfs_add_options
--	 *
--	 * The longest string achievable here is
--	 * ",atime,strictatime,nodev,noexec,rw,nosuid,nomand".
--	 */
--	char opts[64] = "";
--
--	/* atime */
--	if (strcmp(p_atime, "on") == 0) {
--		/* relatime */
--		if (strcmp(p_relatime, "on") == 0)
--			strcat(opts, ",atime,relatime");
--		else if (strcmp(p_relatime, "off") == 0)
--			strcat(opts, ",atime,strictatime");
--		else
--			fprintf(stderr,
--			    PROGNAME "[%d]: %s: invalid relatime=%s\n",
--			    getpid(), dataset, p_relatime);
--	} else if (strcmp(p_atime, "off") == 0) {
--		strcat(opts, ",noatime");
--	} else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid atime=%s\n",
--		    getpid(), dataset, p_atime);
--
--	/* devices */
--	if (strcmp(p_devices, "on") == 0)
--		strcat(opts, ",dev");
--	else if (strcmp(p_devices, "off") == 0)
--		strcat(opts, ",nodev");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid devices=%s\n",
--		    getpid(), dataset, p_devices);
--
--	/* exec */
--	if (strcmp(p_exec, "on") == 0)
--		strcat(opts, ",exec");
--	else if (strcmp(p_exec, "off") == 0)
--		strcat(opts, ",noexec");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid exec=%s\n",
--		    getpid(), dataset, p_exec);
--
--	/* readonly */
--	if (strcmp(p_readonly, "on") == 0)
--		strcat(opts, ",ro");
--	else if (strcmp(p_readonly, "off") == 0)
--		strcat(opts, ",rw");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid readonly=%s\n",
--		    getpid(), dataset, p_readonly);
--
--	/* setuid */
--	if (strcmp(p_setuid, "on") == 0)
--		strcat(opts, ",suid");
--	else if (strcmp(p_setuid, "off") == 0)
--		strcat(opts, ",nosuid");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid setuid=%s\n",
--		    getpid(), dataset, p_setuid);
--
--	/* nbmand */
--	if (strcmp(p_nbmand, "on") == 0)
--		strcat(opts, ",mand");
--	else if (strcmp(p_nbmand, "off") == 0)
--		strcat(opts, ",nomand");
--	else
--		fprintf(stderr, PROGNAME "[%d]: %s: invalid nbmand=%s\n",
--		    getpid(), dataset, p_setuid);
--
--	if (strcmp(p_systemd_wantedby, "-") != 0) {
--		noauto = true;
--
--		if (strcmp(p_systemd_wantedby, "none") != 0)
--			wantedby = p_systemd_wantedby;
--	}
--
--	if (strcmp(p_systemd_requiredby, "-") != 0) {
--		noauto = true;
--
--		if (strcmp(p_systemd_requiredby, "none") != 0)
--			requiredby = p_systemd_requiredby;
--	}
--
--	/*
--	 * For datasets with canmount=on, a dependency is created for
--	 * local-fs.target by default. To avoid regressions, this dependency
--	 * is reduced to "wants" rather than "requires" when nofail!=off.
--	 * **THIS MAY CHANGE**
--	 * noauto=on disables this behavior completely.
--	 */
--	if (!noauto) {
--		if (strcmp(p_systemd_nofail, "off") == 0)
--			requiredby = strdupa("local-fs.target");
--		else {
--			wantedby = strdupa("local-fs.target");
--			wantedby_append = strcmp(p_systemd_nofail, "on") != 0;
--		}
--	}
--
--	/*
--	 * Handle existing files:
--	 * 1.	We never overwrite existing files, although we may delete
--	 * 	files if we're sure they were created by us. (see 5.)
--	 * 2.	We handle files differently based on canmount.
--	 * 	Units with canmount=on always have precedence over noauto.
--	 * 	This is enforced by the noauto_not_on_sem semaphore,
--	 * 	which is only unlocked when the last canmount=on process exits.
--	 * 	It is important to use p_canmount and not noauto here,
--	 * 	since we categorise by canmount while other properties,
--	 * 	e.g. org.openzfs.systemd:wanted-by, also modify noauto.
--	 * 3.	If no unit file exists for a noauto dataset, we create one.
--	 * 	Additionally, we use noauto_files to track the unit file names
--	 * 	(which are the systemd-escaped mountpoints) of all (exclusively)
--	 * 	noauto datasets that had a file created.
--	 * 4.	If the file to be created is found in the tracking array,
--	 * 	we do NOT create it.
--	 * 5.	If a file exists for a noauto dataset,
--	 * 	we check whether the file name is in the array.
--	 * 	If it is, we have multiple noauto datasets for the same
--	 * 	mountpoint. In such cases, we remove the file for safety.
--	 * 	We leave the file name in the tracking array to avoid
--	 * 	further noauto datasets creating a file for this path again.
--	 */
--
--	{
--		sem_t *our_sem = (strcmp(p_canmount, "on") == 0) ?
--		    &noauto_files->noauto_names_sem :
--		    &noauto_files->noauto_not_on_sem;
--		while (sem_wait(our_sem) == -1 && errno == EINTR)
--			;
--	}
--
--	struct stat stbuf;
--	bool already_exists = fstatat(destdir_fd, mountfile, &stbuf, 0) == 0;
--
--	bool is_known = false;
--	for (size_t i = 0; i < noauto_files->noauto_names_len; ++i) {
--		if (strncmp(
--		    noauto_files->noauto_names[i], mountfile, NAME_MAX) == 0) {
--			is_known = true;
--			break;
--		}
--	}
--
--	if (already_exists) {
--		if (is_known) {
--			/* If it's in $noauto_files, we must be noauto too */
--
--			/* See 5 */
--			errno = 0;
--			(void) unlinkat(destdir_fd, mountfile, 0);
--
--			/* See 2 */
--			fprintf(stderr, PROGNAME "[%d]: %s: "
--			    "removing duplicate noauto unit %s%s%s\n",
--			    getpid(), dataset, mountfile,
--			    errno ? "" : " failed: ",
--			    errno ? "" : strerror(errno));
--		} else {
--			/* Don't log for canmount=noauto */
--			if (strcmp(p_canmount, "on") == 0)
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "%s already exists. Skipping.\n",
--				    getpid(), dataset, mountfile);
--		}
--
--		/* File exists: skip current dataset */
--		if (strcmp(p_canmount, "on") == 0)
--			sem_post(&noauto_files->noauto_names_sem);
--		return (0);
--	} else {
--		if (is_known) {
--			/* See 4 */
--			if (strcmp(p_canmount, "on") == 0)
--				sem_post(&noauto_files->noauto_names_sem);
--			return (0);
--		} else if (strcmp(p_canmount, "noauto") == 0) {
--			if (noauto_files->noauto_names_len ==
--			    noauto_files->noauto_names_max)
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "noauto dataset limit (%zu) reached! "
--				    "Not tracking %s. Please report this to "
--				    "https://github.com/openzfs/zfs\n",
--				    getpid(), dataset,
--				    noauto_files->noauto_names_max, mountfile);
--			else {
--				strncpy(noauto_files->noauto_names[
--				    noauto_files->noauto_names_len],
--				    mountfile, NAME_MAX);
--				++noauto_files->noauto_names_len;
--			}
--		}
--	}
--
--
--	FILE *mountfile_f = fopenat(destdir_fd, mountfile,
--	    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w", 0644);
--	if (strcmp(p_canmount, "on") == 0)
--		sem_post(&noauto_files->noauto_names_sem);
--	if (!mountfile_f) {
--		fprintf(stderr,
--		    PROGNAME "[%d]: %s: couldn't open %s under %s: %s\n",
--		    getpid(), dataset, mountfile, destdir, strerror(errno));
--		return (1);
--	}
--
--	fprintf(mountfile_f,
--	    OUTPUT_HEADER
--	    "[Unit]\n"
--	    "SourcePath=" FSLIST "/%s\n"
--	    "Documentation=man:zfs-mount-generator(8)\n"
--	    "\n"
--	    "Before=",
--	    cachefile);
--
--	if (p_systemd_before)
--		fprintf(mountfile_f, "%s ", p_systemd_before);
--	fprintf(mountfile_f, "zfs-mount.service"); /* Ensures we don't race */
--	if (requiredby)
--		fprintf(mountfile_f, " %s", requiredby);
--	if (wantedby && wantedby_append)
--		fprintf(mountfile_f, " %s", wantedby);
--
--	fprintf(mountfile_f,
--	    "\n"
--	    "After=");
--	if (p_systemd_after)
--		fprintf(mountfile_f, "%s ", p_systemd_after);
--	fprintf(mountfile_f, "%s\n", after);
--
--	fprintf(mountfile_f, "Wants=%s\n", wants);
--
--	if (bindsto)
--		fprintf(mountfile_f, "BindsTo=%s\n", bindsto);
--	if (p_systemd_requires)
--		fprintf(mountfile_f, "Requires=%s\n", p_systemd_requires);
--	if (p_systemd_requiresmountsfor)
--		fprintf(mountfile_f,
--		    "RequiresMountsFor=%s\n", p_systemd_requiresmountsfor);
--
--	fprintf(mountfile_f,
--	    "\n"
--	    "[Mount]\n"
--	    "Where=%s\n"
--	    "What=%s\n"
--	    "Type=zfs\n"
--	    "Options=defaults%s,zfsutil\n",
--	    p_mountpoint, dataset, opts);
--
--	(void) fclose(mountfile_f);
--
--	if (!requiredby && !wantedby)
--		return (0);
--
--	/* Finally, create the appropriate dependencies */
--	char *linktgt;
--	if (asprintf(&linktgt, "../%s", mountfile) == -1)
--		EXIT_ENOMEM();
--
--	char *dependencies[][2] = {
--		{"wants", wantedby},
--		{"requires", requiredby},
--		{}
--	};
--	for (__typeof__(&*dependencies) dep = &*dependencies; **dep; ++dep) {
--		if (!(*dep)[1])
--			continue;
--
--		for (char *reqby = strtok_r((*dep)[1], " ", &toktmp);
--		    reqby;
--		    reqby = strtok_r(NULL, " ", &toktmp)) {
--			char *depdir;
--			if (asprintf(&depdir, "%s.%s", reqby, (*dep)[0]) == -1)
--				EXIT_ENOMEM();
--
--			(void) mkdirat(destdir_fd, depdir, 0755);
--			int depdir_fd = openat(destdir_fd, depdir,
--			    O_PATH | O_DIRECTORY | O_CLOEXEC);
--			if (depdir_fd < 0) {
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "couldn't open %s under %s: %s\n",
--				    getpid(), dataset, depdir, destdir,
--				    strerror(errno));
--				free(depdir);
--				continue;
--			}
--
--			if (symlinkat(linktgt, depdir_fd, mountfile) == -1)
--				fprintf(stderr, PROGNAME "[%d]: %s: "
--				    "couldn't symlink at "
--				    "%s under %s under %s: %s\n",
--				    getpid(), dataset, mountfile,
--				    depdir, destdir, strerror(errno));
--
--			(void) close(depdir_fd);
--			free(depdir);
--		}
--	}
--
--	return (0);
--}
--
--
--static int
--pool_enumerator(zpool_handle_t *pool, void *data __attribute__((unused)))
--{
--	int ret = 0;
--
--	/*
--	 * Pools are guaranteed-unique by the kernel,
--	 * no risk of leaking dupes here
--	 */
--	char *name = strdup(zpool_get_name(pool));
--	if (!name || !tsearch(name, &known_pools, STRCMP)) {
--		free(name);
--		ret = ENOMEM;
--	}
--
--	zpool_close(pool);
--	return (ret);
--}
--
--int
--main(int argc, char **argv)
--{
--	struct timespec time_init = {};
--	clock_gettime(CLOCK_MONOTONIC_RAW, &time_init);
--
--	{
--		int kmfd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
--		if (kmfd >= 0) {
--			(void) dup2(kmfd, STDERR_FILENO);
--			(void) close(kmfd);
--		}
--	}
--
--	uint8_t debug = 0;
--
--	argv0 = argv[0];
--	switch (argc) {
--	case 1:
--		/* Use default */
--		break;
--	case 2:
--	case 4:
--		destdir = argv[1];
--		break;
--	default:
--		fprintf(stderr,
--		    PROGNAME "[%d]: wrong argument count: %d\n",
--		    getpid(), argc - 1);
--		_exit(1);
--	}
--
--	{
--		destdir_fd = open(destdir, O_PATH | O_DIRECTORY | O_CLOEXEC);
--		if (destdir_fd < 0) {
--			fprintf(stderr, PROGNAME "[%d]: "
--			    "can't open destination directory %s: %s\n",
--			    getpid(), destdir, strerror(errno));
--			_exit(1);
--		}
--	}
--
--	DIR *fslist_dir = opendir(FSLIST);
--	if (!fslist_dir) {
--		if (errno != ENOENT)
--			fprintf(stderr,
--			    PROGNAME "[%d]: couldn't open " FSLIST ": %s\n",
--			    getpid(), strerror(errno));
--		_exit(0);
--	}
--
--	{
--		libzfs_handle_t *libzfs = libzfs_init();
--		if (libzfs) {
--			if (zpool_iter(libzfs, pool_enumerator, NULL) != 0)
--				fprintf(stderr, PROGNAME "[%d]: "
--				    "error listing pools, ignoring\n",
--				    getpid());
--			libzfs_fini(libzfs);
--		} else
--			fprintf(stderr, PROGNAME "[%d]: "
--			    "couldn't start libzfs, ignoring\n",
--			    getpid());
--	}
--
--	{
--		int regerr = regcomp(&uri_regex, URI_REGEX_S, 0);
--		if (regerr != 0) {
--			fprintf(stderr,
--			    PROGNAME "[%d]: invalid regex: %d\n",
--			    getpid(), regerr);
--			_exit(1);
--		}
--	}
--
--	{
--		/*
--		 * We could just get a gigabyte here and Not Care,
--		 * but if vm.overcommit_memory=2, then MAP_NORESERVE is ignored
--		 * and we'd try (and likely fail) to rip it out of swap
--		 */
--		noauto_files = mmap(NULL, 4 * 1024 * 1024,
--		    PROT_READ | PROT_WRITE,
--		    MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
--		if (noauto_files == MAP_FAILED) {
--			fprintf(stderr,
--			    PROGNAME "[%d]: couldn't allocate IPC region: %s\n",
--			    getpid(), strerror(errno));
--			_exit(1);
--		}
--
--		sem_init(&noauto_files->noauto_not_on_sem, true, 0);
--		sem_init(&noauto_files->noauto_names_sem, true, 1);
--		noauto_files->noauto_names_len = 0;
--		/* Works out to 16447ish, *well* enough */
--		noauto_files->noauto_names_max =
--		    (4 * 1024 * 1024 - sizeof (*noauto_files)) / NAME_MAX;
--	}
--
--	char *line = NULL;
--	size_t linelen = 0;
--	struct timespec time_start = {};
--	{
--		const char *dbgenv = getenv("ZFS_DEBUG");
--		if (dbgenv)
--			debug = atoi(dbgenv);
--		else {
--			FILE *cmdline = fopen("/proc/cmdline", "re");
--			if (cmdline != NULL) {
--				if (getline(&line, &linelen, cmdline) >= 0)
--					debug = strstr(line, "debug") ? 2 : 0;
--				(void) fclose(cmdline);
--			}
--		}
--
--		if (debug && !isatty(STDOUT_FILENO))
--			dup2(STDERR_FILENO, STDOUT_FILENO);
--	}
--
--	size_t forked_canmount_on = 0;
--	size_t forked_canmount_not_on = 0;
--	size_t canmount_on_pids_len = 128;
--	pid_t *canmount_on_pids =
--	    malloc(canmount_on_pids_len * sizeof (*canmount_on_pids));
--	if (canmount_on_pids == NULL)
--		canmount_on_pids_len = 0;
--
--	if (debug)
--		clock_gettime(CLOCK_MONOTONIC_RAW, &time_start);
--
--	ssize_t read;
--	pid_t pid;
--	struct dirent *cachent;
--	while ((cachent = readdir(fslist_dir)) != NULL) {
--		if (strcmp(cachent->d_name, ".") == 0 ||
--		    strcmp(cachent->d_name, "..") == 0)
--			continue;
--
--		FILE *cachefile = fopenat(dirfd(fslist_dir), cachent->d_name,
--		    O_RDONLY | O_CLOEXEC, "r", 0);
--		if (!cachefile) {
--			fprintf(stderr, PROGNAME "[%d]: "
--			    "couldn't open %s under " FSLIST ": %s\n",
--			    getpid(), cachent->d_name, strerror(errno));
--			continue;
--		}
--
--		while ((read = getline(&line, &linelen, cachefile)) >= 0) {
--			line[read - 1] = '\0'; /* newline */
--
--			switch (pid = fork()) {
--			case -1:
--				fprintf(stderr,
--				    PROGNAME "[%d]: couldn't fork for %s: %s\n",
--				    getpid(), line, strerror(errno));
--				break;
--			case 0: /* child */
--				_exit(line_worker(line, cachent->d_name));
--			default: { /* parent */
--				char *tmp;
--				char *dset = strtok_r(line, "\t", &tmp);
--				strtok_r(NULL, "\t", &tmp);
--				char *canmount = strtok_r(NULL, "\t", &tmp);
--				bool canmount_on =
--				    canmount && strncmp(canmount, "on", 2) == 0;
--
--				if (debug >= 2)
--					printf(PROGNAME ": forked %d, "
--					    "canmount_on=%d, dataset=%s\n",
--					    (int)pid, canmount_on, dset);
--
--				if (canmount_on &&
--				    forked_canmount_on ==
--				    canmount_on_pids_len) {
--					size_t new_len =
--					    (canmount_on_pids_len ?: 16) * 2;
--					void *new_pidlist =
--					    realloc(canmount_on_pids,
--					    new_len *
--					    sizeof (*canmount_on_pids));
--					if (!new_pidlist) {
--						fprintf(stderr,
--						    PROGNAME "[%d]: "
--						    "out of memory! "
--						    "Mount ordering may be "
--						    "affected.\n", getpid());
--						continue;
--					}
--
--					canmount_on_pids = new_pidlist;
--					canmount_on_pids_len = new_len;
--				}
--
--				if (canmount_on) {
--					canmount_on_pids[forked_canmount_on] =
--					    pid;
--					++forked_canmount_on;
--				} else
--					++forked_canmount_not_on;
--				break;
--			}
--			}
--		}
--
--		(void) fclose(cachefile);
--	}
--	free(line);
--
--	if (forked_canmount_on == 0) {
--		/* No canmount=on processes to finish, so don't deadlock here */
--		for (size_t i = 0; i < forked_canmount_not_on; ++i)
--			sem_post(&noauto_files->noauto_not_on_sem);
--	} else {
--		/* Likely a no-op, since we got these from a narrow fork loop */
--		qsort(canmount_on_pids, forked_canmount_on,
--		    sizeof (*canmount_on_pids), PID_T_CMP);
--	}
--
--	int status, ret = 0;
--	struct rusage usage;
--	size_t forked_canmount_on_max = forked_canmount_on;
--	while ((pid = wait4(-1, &status, 0, &usage)) != -1) {
--		ret |= WEXITSTATUS(status) | WTERMSIG(status);
--
--		if (forked_canmount_on != 0) {
--			if (bsearch(&pid, canmount_on_pids,
--			    forked_canmount_on_max, sizeof (*canmount_on_pids),
--			    PID_T_CMP))
--				--forked_canmount_on;
--
--			if (forked_canmount_on == 0) {
--				/*
--				 * All canmount=on processes have finished,
--				 * let all the lower-priority ones finish now
--				 */
--				for (size_t i = 0;
--				    i < forked_canmount_not_on; ++i)
--					sem_post(
--					    &noauto_files->noauto_not_on_sem);
--			}
--		}
--
--		if (debug >= 2)
--			printf(PROGNAME ": %d done, user=%llu.%06us, "
--			    "system=%llu.%06us, maxrss=%ldB, ex=0x%x\n",
--			    (int)pid,
--			    (unsigned long long) usage.ru_utime.tv_sec,
--			    (unsigned int) usage.ru_utime.tv_usec,
--			    (unsigned long long) usage.ru_stime.tv_sec,
--			    (unsigned int) usage.ru_stime.tv_usec,
--			    usage.ru_maxrss * 1024, status);
--	}
--
--	if (debug) {
--		struct timespec time_end = {};
--		clock_gettime(CLOCK_MONOTONIC_RAW, &time_end);
--
--		getrusage(RUSAGE_SELF, &usage);
--		printf(
--		    "\n"
--		    PROGNAME ": self    : "
--		    "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
--		    (unsigned long long) usage.ru_utime.tv_sec,
--		    (unsigned int) usage.ru_utime.tv_usec,
--		    (unsigned long long) usage.ru_stime.tv_sec,
--		    (unsigned int) usage.ru_stime.tv_usec,
--		    usage.ru_maxrss * 1024);
--
--		getrusage(RUSAGE_CHILDREN, &usage);
--		printf(PROGNAME ": children: "
--		    "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
--		    (unsigned long long) usage.ru_utime.tv_sec,
--		    (unsigned int) usage.ru_utime.tv_usec,
--		    (unsigned long long) usage.ru_stime.tv_sec,
--		    (unsigned int) usage.ru_stime.tv_usec,
--		    usage.ru_maxrss * 1024);
--
--		if (time_start.tv_nsec > time_end.tv_nsec) {
--			time_end.tv_nsec =
--			    1000000000 + time_end.tv_nsec - time_start.tv_nsec;
--			time_end.tv_sec -= 1;
--		} else
--			time_end.tv_nsec -= time_start.tv_nsec;
--		time_end.tv_sec -= time_start.tv_sec;
--
--		if (time_init.tv_nsec > time_start.tv_nsec) {
--			time_start.tv_nsec =
--			    1000000000 + time_start.tv_nsec - time_init.tv_nsec;
--			time_start.tv_sec -= 1;
--		} else
--			time_start.tv_nsec -= time_init.tv_nsec;
--		time_start.tv_sec -= time_init.tv_sec;
--
--		time_init.tv_nsec = time_start.tv_nsec + time_end.tv_nsec;
--		time_init.tv_sec =
--		    time_start.tv_sec + time_end.tv_sec +
--		    time_init.tv_nsec / 1000000000;
--		time_init.tv_nsec %= 1000000000;
--
--		printf(PROGNAME ": wall    : "
--		    "total=%llu.%09llus = "
--		    "init=%llu.%09llus + real=%llu.%09llus\n",
--		    (unsigned long long) time_init.tv_sec,
--		    (unsigned long long) time_init.tv_nsec,
--		    (unsigned long long) time_start.tv_sec,
--		    (unsigned long long) time_start.tv_nsec,
--		    (unsigned long long) time_end.tv_sec,
--		    (unsigned long long) time_end.tv_nsec);
--	}
--
--	_exit(ret);
--}
-diff --git a/etc/systemd/system-generators/zfs-mount-generator.in b/etc/systemd/system-generators/zfs-mount-generator.in
-new file mode 100755
-index 0000000000..c276fbbce5
---- /dev/null
-+++ b/etc/systemd/system-generators/zfs-mount-generator.in
-@@ -0,0 +1,474 @@
-+#!/bin/sh
-+
-+# zfs-mount-generator - generates systemd mount units for zfs
-+# Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
-+# Copyright (c) 2020 InsanePrawn <insane.prawny@gmail.com>
-+#
-+# Permission is hereby granted, free of charge, to any person obtaining
-+# a copy of this software and associated documentation files (the
-+# "Software"), to deal in the Software without restriction, including
-+# without limitation the rights to use, copy, modify, merge, publish,
-+# distribute, sublicense, and/or sell copies of the Software, and to
-+# permit persons to whom the Software is furnished to do so, subject to
-+# the following conditions:
-+#
-+# The above copyright notice and this permission notice shall be
-+# included in all copies or substantial portions of the Software.
-+#
-+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-+
-+set -e
-+
-+FSLIST="@sysconfdir@/zfs/zfs-list.cache"
-+
-+[ -d "${FSLIST}" ] || exit 0
-+[ "$(echo "${FSLIST}"/*)" = "${FSLIST}/*" ] && exit 0
-+
-+do_fail() {
-+  printf 'zfs-mount-generator: %s\n' "$*" > /dev/kmsg
-+  exit 1
-+}
-+
-+# test if $1 is in space-separated list $2
-+is_known() {
-+  query="$1"
-+  IFS=' '
-+  for element in $2 ; do
-+    if [ "$query" = "$element" ] ; then
-+      return 0
-+    fi
-+  done
-+  return 1
-+}
-+
-+# create dependency on unit file $1
-+# of type $2, i.e. "wants" or "requires"
-+# in the target units from space-separated list $3
-+create_dependencies() {
-+  unitfile="$1"
-+  suffix="$2"
-+  IFS=' '
-+  for target in $3 ; do
-+    target_dir="${dest_norm}/${target}.${suffix}/"
-+    mkdir -p "${target_dir}"
-+    ln -s "../${unitfile}" "${target_dir}"
-+  done
-+}
-+
-+# see systemd.generator
-+if [ $# -eq 0 ] ; then
-+  dest_norm="/tmp"
-+elif [ $# -eq 3 ] ; then
-+  dest_norm="${1}"
-+else
-+  do_fail "zero or three arguments required"
-+fi
-+
-+pools=$(zpool list -H -o name || true)
-+
-+# All needed information about each ZFS is available from
-+# zfs list -H -t filesystem -o <properties>
-+# cached in $FSLIST, and each line is processed by the following function:
-+# See the list below for the properties and their order
-+
-+process_line() {
-+
-+  # zfs list -H -o name,...
-+  # fields are tab separated
-+  IFS="$(printf '\t')"
-+  # shellcheck disable=SC2086
-+  set -- $1
-+
-+  dataset="${1}"
-+  pool="${dataset%%/*}"
-+  p_mountpoint="${2}"
-+  p_canmount="${3}"
-+  p_atime="${4}"
-+  p_relatime="${5}"
-+  p_devices="${6}"
-+  p_exec="${7}"
-+  p_readonly="${8}"
-+  p_setuid="${9}"
-+  p_nbmand="${10}"
-+  p_encroot="${11}"
-+  p_keyloc="${12}"
-+  p_systemd_requires="${13}"
-+  p_systemd_requiresmountsfor="${14}"
-+  p_systemd_before="${15}"
-+  p_systemd_after="${16}"
-+  p_systemd_wantedby="${17}"
-+  p_systemd_requiredby="${18}"
-+  p_systemd_nofail="${19}"
-+  p_systemd_ignore="${20}"
-+
-+  # Minimal pre-requisites to mount a ZFS dataset
-+  # By ordering before zfs-mount.service, we avoid race conditions.
-+  after="zfs-import.target"
-+  before="zfs-mount.service"
-+  wants="zfs-import.target"
-+  requires=""
-+  requiredmounts=""
-+  bindsto=""
-+  wantedby=""
-+  requiredby=""
-+  noauto="off"
-+
-+  # If the pool is already imported, zfs-import.target is not needed.  This
-+  # avoids a dependency loop on root-on-ZFS systems:
-+  # systemd-random-seed.service After (via RequiresMountsFor) var-lib.mount
-+  # After zfs-import.target After zfs-import-{cache,scan}.service After
-+  # cryptsetup.service After systemd-random-seed.service.
-+  #
-+  # Pools are newline-separated and may contain spaces in their names.
-+  # There is no better portable way to set IFS to just a newline.  Using
-+  # $(printf '\n') doesn't work because $(...) strips trailing newlines.
-+  IFS="
-+"
-+  for p in $pools ; do
-+    if [ "$p" = "$pool" ] ; then
-+      after=""
-+      wants=""
-+      break
-+    fi
-+  done
-+
-+  if [ -n "${p_systemd_after}" ] && \
-+      [ "${p_systemd_after}" != "-" ] ; then
-+    after="${p_systemd_after} ${after}"
-+  fi
-+
-+  if [ -n "${p_systemd_before}" ] && \
-+      [ "${p_systemd_before}" != "-" ] ; then
-+    before="${p_systemd_before} ${before}"
-+  fi
-+
-+  if [ -n "${p_systemd_requires}" ] && \
-+      [ "${p_systemd_requires}" != "-" ] ; then
-+    requires="Requires=${p_systemd_requires}"
-+  fi
-+
-+  if [ -n "${p_systemd_requiresmountsfor}" ] && \
-+      [ "${p_systemd_requiresmountsfor}" != "-" ] ; then
-+    requiredmounts="RequiresMountsFor=${p_systemd_requiresmountsfor}"
-+  fi
-+
-+  # Handle encryption
-+  if [ -n "${p_encroot}" ] &&
-+      [ "${p_encroot}" != "-" ] ; then
-+    keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
-+    if [ "${p_encroot}" = "${dataset}" ] ; then
-+      keymountdep=""
-+      if [ "${p_keyloc%%://*}" = "file" ] ; then
-+        if [ -n "${requiredmounts}" ] ; then
-+          keymountdep="${requiredmounts} '${p_keyloc#file://}'"
-+        else
-+          keymountdep="RequiresMountsFor='${p_keyloc#file://}'"
-+        fi
-+        keyloadscript="@sbindir@/zfs load-key \"${dataset}\""
-+      elif [ "${p_keyloc}" = "prompt" ] ; then
-+        keyloadscript="\
-+count=0;\
-+while [ \$\$count -lt 3 ];do\
-+  systemd-ask-password --id=\"zfs:${dataset}\"\
-+    \"Enter passphrase for ${dataset}:\"|\
-+    @sbindir@/zfs load-key \"${dataset}\" && exit 0;\
-+  count=\$\$((count + 1));\
-+done;\
-+exit 1"
-+      else
-+        printf 'zfs-mount-generator: (%s) invalid keylocation\n' \
-+          "${dataset}" >/dev/kmsg
-+      fi
-+      keyloadcmd="\
-+/bin/sh -c '\
-+set -eu;\
-+keystatus=\"\$\$(@sbindir@/zfs get -H -o value keystatus \"${dataset}\")\";\
-+[ \"\$\$keystatus\" = \"unavailable\" ] || exit 0;\
-+${keyloadscript}'"
-+      keyunloadcmd="\
-+/bin/sh -c '\
-+set -eu;\
-+keystatus=\"\$\$(@sbindir@/zfs get -H -o value keystatus \"${dataset}\")\";\
-+[ \"\$\$keystatus\" = \"available\" ] || exit 0;\
-+@sbindir@/zfs unload-key \"${dataset}\"'"
-+
-+
-+
-+      # Generate the key-load .service unit
-+      #
-+      # Note: It is tempting to use a `<<EOF` style here-document for this, but
-+      #   bash requires a writable /tmp or $TMPDIR for that. This is not always
-+      #   available early during boot.
-+      #
-+      echo \
-+"# Automatically generated by zfs-mount-generator
-+
-+[Unit]
-+Description=Load ZFS key for ${dataset}
-+SourcePath=${cachefile}
-+Documentation=man:zfs-mount-generator(8)
-+DefaultDependencies=no
-+Wants=${wants}
-+After=${after}
-+${requires}
-+${keymountdep}
-+
-+[Service]
-+Type=oneshot
-+RemainAfterExit=yes
-+# This avoids a dependency loop involving systemd-journald.socket if this
-+# dataset is a parent of the root filesystem.
-+StandardOutput=null
-+StandardError=null
-+ExecStart=${keyloadcmd}
-+ExecStop=${keyunloadcmd}"   > "${dest_norm}/${keyloadunit}"
-+    fi
-+    # Update the dependencies for the mount file to want the
-+    # key-loading unit.
-+    wants="${wants}"
-+    bindsto="BindsTo=${keyloadunit}"
-+    after="${after} ${keyloadunit}"
-+  fi
-+
-+  # Prepare the .mount unit
-+
-+  # skip generation of the mount unit if org.openzfs.systemd:ignore is "on"
-+  if [ -n "${p_systemd_ignore}" ] ; then
-+    if [ "${p_systemd_ignore}" = "on" ] ; then
-+      return
-+    elif [ "${p_systemd_ignore}" = "-" ] \
-+      || [ "${p_systemd_ignore}" = "off" ] ; then
-+      : # This is OK
-+    else
-+      do_fail "invalid org.openzfs.systemd:ignore for ${dataset}"
-+    fi
-+  fi
-+
-+  # Check for canmount=off .
-+  if [ "${p_canmount}" = "off" ] ; then
-+    return
-+  elif [ "${p_canmount}" = "noauto" ] ; then
-+    noauto="on"
-+  elif [ "${p_canmount}" = "on" ] ; then
-+    : # This is OK
-+  else
-+    do_fail "invalid canmount for ${dataset}"
-+  fi
-+
-+  # Check for legacy and blank mountpoints.
-+  if [ "${p_mountpoint}" = "legacy" ] ; then
-+    return
-+  elif [ "${p_mountpoint}" = "none" ] ; then
-+    return
-+  elif [ "${p_mountpoint%"${p_mountpoint#?}"}" != "/" ] ; then
-+    do_fail "invalid mountpoint for ${dataset}"
-+  fi
-+
-+  # Escape the mountpoint per systemd policy.
-+  mountfile="$(systemd-escape --path --suffix=mount "${p_mountpoint}")"
-+
-+  # Parse options
-+  # see lib/libzfs/libzfs_mount.c:zfs_add_options
-+  opts=""
-+
-+  # atime
-+  if [ "${p_atime}" = on ] ; then
-+    # relatime
-+    if [ "${p_relatime}" = on ] ; then
-+      opts="${opts},atime,relatime"
-+    elif [ "${p_relatime}" = off ] ; then
-+      opts="${opts},atime,strictatime"
-+    else
-+      printf 'zfs-mount-generator: (%s) invalid relatime\n' \
-+        "${dataset}" >/dev/kmsg
-+    fi
-+  elif [ "${p_atime}" = off ] ; then
-+    opts="${opts},noatime"
-+  else
-+    printf 'zfs-mount-generator: (%s) invalid atime\n' \
-+      "${dataset}" >/dev/kmsg
-+  fi
-+
-+  # devices
-+  if [ "${p_devices}" = on ] ; then
-+    opts="${opts},dev"
-+  elif [ "${p_devices}" = off ] ; then
-+    opts="${opts},nodev"
-+  else
-+    printf 'zfs-mount-generator: (%s) invalid devices\n' \
-+      "${dataset}" >/dev/kmsg
-+  fi
-+
-+  # exec
-+  if [ "${p_exec}" = on ] ; then
-+    opts="${opts},exec"
-+  elif [ "${p_exec}" = off ] ; then
-+    opts="${opts},noexec"
-+  else
-+    printf 'zfs-mount-generator: (%s) invalid exec\n' \
-+      "${dataset}" >/dev/kmsg
-+  fi
-+
-+  # readonly
-+  if [ "${p_readonly}" = on ] ; then
-+    opts="${opts},ro"
-+  elif [ "${p_readonly}" = off ] ; then
-+    opts="${opts},rw"
-+  else
-+    printf 'zfs-mount-generator: (%s) invalid readonly\n' \
-+      "${dataset}" >/dev/kmsg
-+  fi
-+
-+  # setuid
-+  if [ "${p_setuid}" = on ] ; then
-+    opts="${opts},suid"
-+  elif [ "${p_setuid}" = off ] ; then
-+    opts="${opts},nosuid"
-+  else
-+    printf 'zfs-mount-generator: (%s) invalid setuid\n' \
-+      "${dataset}" >/dev/kmsg
-+  fi
-+
-+  # nbmand
-+  if [ "${p_nbmand}" = on ]  ; then
-+    opts="${opts},mand"
-+  elif [ "${p_nbmand}" = off ] ; then
-+    opts="${opts},nomand"
-+  else
-+    printf 'zfs-mount-generator: (%s) invalid nbmand\n' \
-+      "${dataset}" >/dev/kmsg
-+  fi
-+
-+  if [ -n "${p_systemd_wantedby}" ] && \
-+      [ "${p_systemd_wantedby}" != "-" ] ; then
-+    noauto="on"
-+    if [ "${p_systemd_wantedby}" = "none" ] ; then
-+      wantedby=""
-+    else
-+      wantedby="${p_systemd_wantedby}"
-+      before="${before} ${wantedby}"
-+    fi
-+  fi
-+
-+  if [ -n "${p_systemd_requiredby}" ] && \
-+      [ "${p_systemd_requiredby}" != "-" ] ; then
-+    noauto="on"
-+    if [ "${p_systemd_requiredby}" = "none" ] ; then
-+      requiredby=""
-+    else
-+      requiredby="${p_systemd_requiredby}"
-+      before="${before} ${requiredby}"
-+    fi
-+  fi
-+
-+  # For datasets with canmount=on, a dependency is created for
-+  # local-fs.target by default. To avoid regressions, this dependency
-+  # is reduced to "wants" rather than "requires" when nofail is not "off".
-+  # **THIS MAY CHANGE**
-+  # noauto=on disables this behavior completely.
-+  if [ "${noauto}" != "on" ] ; then
-+    if [ "${p_systemd_nofail}" = "off" ] ; then
-+      requiredby="local-fs.target"
-+      before="${before} local-fs.target"
-+    else
-+      wantedby="local-fs.target"
-+      if [ "${p_systemd_nofail}" != "on" ] ; then
-+        before="${before} local-fs.target"
-+      fi
-+    fi
-+  fi
-+
-+  # Handle existing files:
-+  # 1.  We never overwrite existing files, although we may delete
-+  #     files if we're sure they were created by us. (see 5.)
-+  # 2.  We handle files differently based on canmount. Units with canmount=on
-+  #     always have precedence over noauto. This is enforced by the sort pipe
-+  #     in the loop around this function.
-+  #     It is important to use $p_canmount and not $noauto here, since we
-+  #     sort by canmount while other properties also modify $noauto, e.g.
-+  #     org.openzfs.systemd:wanted-by.
-+  # 3.  If no unit file exists for a noauto dataset, we create one.
-+  #     Additionally, we use $noauto_files to track the unit file names
-+  #     (which are the systemd-escaped mountpoints) of all (exclusively)
-+  #     noauto datasets that had a file created.
-+  # 4.  If the file to be created is found in the tracking variable,
-+  #     we do NOT create it.
-+  # 5.  If a file exists for a noauto dataset, we check whether the file
-+  #     name is in the variable. If it is, we have multiple noauto datasets
-+  #     for the same mountpoint. In such cases, we remove the file for safety.
-+  #     To avoid further noauto datasets creating a file for this path again,
-+  #     we leave the file name in the tracking variable.
-+  if [ -e "${dest_norm}/${mountfile}" ] ; then
-+    if is_known "$mountfile" "$noauto_files" ; then
-+      # if it's in $noauto_files, we must be noauto too. See 2.
-+      printf 'zfs-mount-generator: removing duplicate noauto %s\n' \
-+        "${mountfile}" >/dev/kmsg
-+      # See 5.
-+      rm "${dest_norm}/${mountfile}"
-+    else
-+      # don't log for canmount=noauto
-+      if [  "${p_canmount}" = "on" ] ; then
-+        printf 'zfs-mount-generator: %s already exists. Skipping.\n' \
-+          "${mountfile}" >/dev/kmsg
-+      fi
-+    fi
-+    # file exists; Skip current dataset.
-+    return
-+  else
-+    if is_known "${mountfile}" "${noauto_files}" ; then
-+      # See 4.
-+      return
-+    elif [ "${p_canmount}" = "noauto" ] ; then
-+      noauto_files="${mountfile} ${noauto_files}"
-+    fi
-+  fi
-+
-+  # Create the .mount unit file.
-+  #
-+  # (Do not use `<<EOF`-style here-documents for this, see warning above)
-+  #
-+  echo \
-+"# Automatically generated by zfs-mount-generator
-+
-+[Unit]
-+SourcePath=${cachefile}
-+Documentation=man:zfs-mount-generator(8)
-+
-+Before=${before}
-+After=${after}
-+Wants=${wants}
-+${bindsto}
-+${requires}
-+${requiredmounts}
-+
-+[Mount]
-+Where=${p_mountpoint}
-+What=${dataset}
-+Type=zfs
-+Options=defaults${opts},zfsutil" > "${dest_norm}/${mountfile}"
-+
-+  # Finally, create the appropriate dependencies
-+  create_dependencies "${mountfile}" "wants" "$wantedby"
-+  create_dependencies "${mountfile}" "requires" "$requiredby"
-+
-+}
-+
-+for cachefile in "${FSLIST}/"* ; do
-+  # Disable glob expansion to protect against special characters when parsing.
-+  set -f
-+  # Sort cachefile's lines by canmount, "on" before "noauto"
-+  # and feed each line into process_line
-+  sort -t "$(printf '\t')" -k 3 -r "${cachefile}" | \
-+  ( # subshell is necessary for `sort|while read` and $noauto_files
-+    noauto_files=""
-+    while read -r fs ; do
-+      process_line "${fs}"
-+    done
-+  )
-+done
--- 
-2.32.0
-
diff -pruN 2.3.4-1/debian/patches/ubuntu/0003-enable-linux-experimental.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0003-enable-linux-experimental.patch
--- 2.3.4-1/debian/patches/ubuntu/0003-enable-linux-experimental.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0003-enable-linux-experimental.patch	2025-09-04 19:31:05.000000000 +0000
@@ -0,0 +1,29 @@
+From d85062b52f77f44ce924ed542211361512df943b Mon Sep 17 00:00:00 2001
+From: John Cabaj <john.cabaj@canonical.com>
+Date: Tue, 11 Mar 2025 13:52:58 -0500
+Subject: [PATCH] Enable linux-experimental support
+
+To build against advance kernels, --enable-linux-experimental
+must be set
+
+Last-Update: 2025-03-11
+Signed-off-by: John Cabaj <john.cabaj@canonical.com>
+---
+ scripts/dkms.mkconf | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/scripts/dkms.mkconf b/scripts/dkms.mkconf
+index 046ce9edc..ec6373739 100755
+--- a/scripts/dkms.mkconf
++++ b/scripts/dkms.mkconf
+@@ -60,6 +60,7 @@ PRE_BUILD="configure
+       fi
+     }
+   )
++  --enable-linux-experimental
+ "
+ POST_BUILD="scripts/dkms.postbuild
+   -n \${PACKAGE_NAME}
+-- 
+2.43.0
+
diff -pruN 2.3.4-1/debian/patches/ubuntu/0004-Linux-6.17-add-config-kernel-dentry-operations-m4.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0004-Linux-6.17-add-config-kernel-dentry-operations-m4.patch
--- 2.3.4-1/debian/patches/ubuntu/0004-Linux-6.17-add-config-kernel-dentry-operations-m4.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0004-Linux-6.17-add-config-kernel-dentry-operations-m4.patch	2025-09-04 19:31:05.000000000 +0000
@@ -0,0 +1,65 @@
+Description: Adding config/kernel-dentry-operations.m4
+Author: John Cabaj <john.cabaj@canonical.com>
+Origin: other
+Forwarded: not-needed
+Last-Update: 2025-08-26
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- /dev/null
++++ b/config/kernel-dentry-operations.m4
+@@ -0,0 +1,55 @@
++dnl #
++dnl # 2.6.28 API change
++dnl # Added d_obtain_alias() helper function.
++dnl #
++AC_DEFUN([ZFS_AC_KERNEL_SRC_D_OBTAIN_ALIAS], [
++	ZFS_LINUX_TEST_SRC([d_obtain_alias], [
++		#include <linux/dcache.h>
++	], [
++		d_obtain_alias(NULL);
++	])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_D_OBTAIN_ALIAS], [
++	AC_MSG_CHECKING([whether d_obtain_alias() is available])
++	ZFS_LINUX_TEST_RESULT_SYMBOL([d_obtain_alias],
++	    [d_obtain_alias], [fs/dcache.c], [
++		AC_MSG_RESULT(yes)
++	], [
++		ZFS_LINUX_TEST_ERROR([d_obtain_alias()])
++	])
++])
++
++dnl #
++dnl # 2.6.38 API change
++dnl # Added d_set_d_op() helper function.
++dnl #
++AC_DEFUN([ZFS_AC_KERNEL_SRC_D_SET_D_OP], [
++	ZFS_LINUX_TEST_SRC([d_set_d_op], [
++		#include <linux/dcache.h>
++	], [
++		d_set_d_op(NULL, NULL);
++	])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_D_SET_D_OP], [
++	AC_MSG_CHECKING([whether d_set_d_op() is available])
++	ZFS_LINUX_TEST_RESULT_SYMBOL([d_set_d_op],
++	    [d_set_d_op], [fs/dcache.c], [
++		AC_MSG_RESULT(yes)
++	], [
++		ZFS_LINUX_TEST_ERROR([d_set_d_op])
++	])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_SRC_DENTRY], [
++        ZFS_AC_KERNEL_SRC_D_OBTAIN_ALIAS
++        ZFS_AC_KERNEL_SRC_D_SET_D_OP
++        ZFS_AC_KERNEL_SRC_S_D_OP
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_DENTRY], [
++        ZFS_AC_KERNEL_D_OBTAIN_ALIAS
++        ZFS_AC_KERNEL_D_SET_D_OP
++        ZFS_AC_KERNEL_S_D_OP
++])
diff -pruN 2.3.4-1/debian/patches/ubuntu/0005-Linux-6.17-compat-config-restore-ZFS_AC_KERNEL_DENTRY-tests.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0005-Linux-6.17-compat-config-restore-ZFS_AC_KERNEL_DENTRY-tests.patch
--- 2.3.4-1/debian/patches/ubuntu/0005-Linux-6.17-compat-config-restore-ZFS_AC_KERNEL_DENTRY-tests.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0005-Linux-6.17-compat-config-restore-ZFS_AC_KERNEL_DENTRY-tests.patch	2025-09-04 19:31:05.000000000 +0000
@@ -0,0 +1,53 @@
+From 5a1981a681fc40e66af9773bc2e887e4e69b2871 Mon Sep 17 00:00:00 2001
+From: Rob Norris <robn@despairlabs.com>
+Date: Thu, 31 Jul 2025 12:38:30 +1000
+Subject: [PATCH] config: restore ZFS_AC_KERNEL_DENTRY tests
+
+Accidentally removed calls in ed048fdc5b.
+
+Sponsored-by: https://despairlabs.com/sponsor/
+Signed-off-by: Rob Norris <robn@despairlabs.com>
+
+Last-Update: 2025-08-26
+Signed-off-by: John Cabaj <john.cabaj@canonical.com>
+---
+ config/kernel-dentry-operations.m4 | 2 --
+ config/kernel.m4                   | 2 ++
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/config/kernel-dentry-operations.m4 b/config/kernel-dentry-operations.m4
+index aa5a9f2aff39..5a5c93b1eee9 100644
+--- a/config/kernel-dentry-operations.m4
++++ b/config/kernel-dentry-operations.m4
+@@ -45,11 +45,9 @@ AC_DEFUN([ZFS_AC_KERNEL_D_SET_D_OP], [
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_DENTRY], [
+         ZFS_AC_KERNEL_SRC_D_OBTAIN_ALIAS
+         ZFS_AC_KERNEL_SRC_D_SET_D_OP
+-        ZFS_AC_KERNEL_SRC_S_D_OP
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_DENTRY], [
+         ZFS_AC_KERNEL_D_OBTAIN_ALIAS
+         ZFS_AC_KERNEL_D_SET_D_OP
+-        ZFS_AC_KERNEL_S_D_OP
+ ])
+diff --git a/config/kernel.m4 b/config/kernel.m4
+index e3e7625db7d8..35819e4d68c5 100644
+--- a/config/kernel.m4
++++ b/config/kernel.m4
+@@ -70,6 +70,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
+ 	ZFS_AC_KERNEL_SRC_COMMIT_METADATA
+ 	ZFS_AC_KERNEL_SRC_SETATTR_PREPARE
+ 	ZFS_AC_KERNEL_SRC_INSERT_INODE_LOCKED
++	ZFS_AC_KERNEL_SRC_DENTRY
+ 	ZFS_AC_KERNEL_SRC_TRUNCATE_SETSIZE
+ 	ZFS_AC_KERNEL_SRC_SECURITY_INODE
+ 	ZFS_AC_KERNEL_SRC_FST_MOUNT
+@@ -188,6 +189,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
+ 	ZFS_AC_KERNEL_COMMIT_METADATA
+ 	ZFS_AC_KERNEL_SETATTR_PREPARE
+ 	ZFS_AC_KERNEL_INSERT_INODE_LOCKED
++	ZFS_AC_KERNEL_DENTRY
+ 	ZFS_AC_KERNEL_TRUNCATE_SETSIZE
+ 	ZFS_AC_KERNEL_SECURITY_INODE
+ 	ZFS_AC_KERNEL_FST_MOUNT
diff -pruN 2.3.4-1/debian/patches/ubuntu/0006-Linux-6.17-compat-d_set_d_op-is-no-longer-available.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/0006-Linux-6.17-compat-d_set_d_op-is-no-longer-available.patch
--- 2.3.4-1/debian/patches/ubuntu/0006-Linux-6.17-compat-d_set_d_op-is-no-longer-available.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/0006-Linux-6.17-compat-d_set_d_op-is-no-longer-available.patch	2025-09-04 19:31:05.000000000 +0000
@@ -0,0 +1,173 @@
+From 9b83cfcb37f6f29d182e32004d47bb3a20038c2a Mon Sep 17 00:00:00 2001
+From: Rob Norris <robn@despairlabs.com>
+Date: Thu, 31 Jul 2025 13:12:43 +1000
+Subject: [PATCH] Linux 6.17: d_set_d_op() is no longer available
+
+We only have extremely narrow uses, so move it all into a single
+function that does only what we need, with and without d_set_d_op().
+
+Sponsored-by: https://despairlabs.com/sponsor/
+Signed-off-by: Rob Norris <robn@despairlabs.com>
+
+Last-Update: 2025-08-26
+Signed-off-by: John Cabaj <john.cabaj@canonical.com>
+---
+ config/kernel-dentry-operations.m4            | 10 +++-
+ include/os/linux/kernel/linux/dcache_compat.h | 26 ---------
+ module/os/linux/zfs/zpl_ctldir.c              | 55 ++++++++++++++++---
+ 3 files changed, 55 insertions(+), 36 deletions(-)
+
+diff --git a/config/kernel-dentry-operations.m4 b/config/kernel-dentry-operations.m4
+index 5a5c93b1eee9..6d87ad0e0710 100644
+--- a/config/kernel-dentry-operations.m4
++++ b/config/kernel-dentry-operations.m4
+@@ -24,6 +24,9 @@ dnl #
+ dnl # 2.6.38 API change
+ dnl # Added d_set_d_op() helper function.
+ dnl #
++dnl # 6.17 API change
++dnl # d_set_d_op() removed. No direct replacement.
++dnl #
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_D_SET_D_OP], [
+ 	ZFS_LINUX_TEST_SRC([d_set_d_op], [
+ 		#include <linux/dcache.h>
+@@ -34,11 +37,12 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_D_SET_D_OP], [
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_D_SET_D_OP], [
+ 	AC_MSG_CHECKING([whether d_set_d_op() is available])
+-	ZFS_LINUX_TEST_RESULT_SYMBOL([d_set_d_op],
+-	    [d_set_d_op], [fs/dcache.c], [
++	ZFS_LINUX_TEST_RESULT([d_set_d_op], [
+ 		AC_MSG_RESULT(yes)
++		AC_DEFINE(HAVE_D_SET_D_OP, 1,
++		    [Define if d_set_d_op() is available])
+ 	], [
+-		ZFS_LINUX_TEST_ERROR([d_set_d_op])
++		AC_MSG_RESULT(no)
+ 	])
+ ])
+ 
+diff --git a/include/os/linux/kernel/linux/dcache_compat.h b/include/os/linux/kernel/linux/dcache_compat.h
+index 16e8a319a5f8..152e5a606f0e 100644
+--- a/include/os/linux/kernel/linux/dcache_compat.h
++++ b/include/os/linux/kernel/linux/dcache_compat.h
+@@ -60,32 +60,6 @@
+ 	} while (0)
+ #endif
+ 
+-/*
+- * 2.6.30 API change,
+- * The const keyword was added to the 'struct dentry_operations' in
+- * the dentry structure.  To handle this we define an appropriate
+- * dentry_operations_t typedef which can be used.
+- */
+-typedef const struct dentry_operations	dentry_operations_t;
+-
+-/*
+- * 2.6.38 API addition,
+- * Added d_clear_d_op() helper function which clears some flags and the
+- * registered dentry->d_op table.  This is required because d_set_d_op()
+- * issues a warning when the dentry operations table is already set.
+- * For the .zfs control directory to work properly we must be able to
+- * override the default operations table and register custom .d_automount
+- * and .d_revalidate callbacks.
+- */
+-static inline void
+-d_clear_d_op(struct dentry *dentry)
+-{
+-	dentry->d_op = NULL;
+-	dentry->d_flags &= ~(
+-	    DCACHE_OP_HASH | DCACHE_OP_COMPARE |
+-	    DCACHE_OP_REVALIDATE | DCACHE_OP_DELETE);
+-}
+-
+ /*
+  * Walk and invalidate all dentry aliases of an inode
+  * unless it's a mountpoint
+diff --git a/module/os/linux/zfs/zpl_ctldir.c b/module/os/linux/zfs/zpl_ctldir.c
+index 48dae79a2373..81ac26cb0c93 100644
+--- a/module/os/linux/zfs/zpl_ctldir.c
++++ b/module/os/linux/zfs/zpl_ctldir.c
+@@ -202,7 +202,7 @@ zpl_snapdir_revalidate(struct dentry *dentry, unsigned int flags)
+ 	return (!!dentry->d_inode);
+ }
+ 
+-static dentry_operations_t zpl_dops_snapdirs = {
++static const struct dentry_operations zpl_dops_snapdirs = {
+ /*
+  * Auto mounting of snapshots is only supported for 2.6.37 and
+  * newer kernels.  Prior to this kernel the ops->follow_link()
+@@ -215,6 +215,51 @@ static dentry_operations_t zpl_dops_snapdirs = {
+ 	.d_revalidate	= zpl_snapdir_revalidate,
+ };
+ 
++/*
++ * For the .zfs control directory to work properly we must be able to override
++ * the default operations table and register custom .d_automount and
++ * .d_revalidate callbacks.
++ */
++static void
++set_snapdir_dentry_ops(struct dentry *dentry, unsigned int extraflags) {
++	static const unsigned int op_flags =
++	    DCACHE_OP_HASH | DCACHE_OP_COMPARE |
++	    DCACHE_OP_REVALIDATE | DCACHE_OP_DELETE |
++	    DCACHE_OP_PRUNE | DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_REAL;
++
++#ifdef HAVE_D_SET_D_OP
++	/*
++	 * d_set_d_op() will set the DCACHE_OP_ flags according to what it
++	 * finds in the passed dentry_operations, so we don't have to.
++	 *
++	 * We clear the flags and the old op table before calling d_set_d_op()
++	 * because issues a warning when the dentry operations table is already
++	 * set.
++	 */
++	dentry->d_op = NULL;
++	dentry->d_flags &= ~op_flags;
++	d_set_d_op(dentry, &zpl_dops_snapdirs);
++	dentry->d_flags |= extraflags;
++#else
++	/*
++	 * Since 6.17 there's no exported way to modify dentry ops, so we have
++	 * to reach in and do it ourselves. This should be safe for our very
++	 * narrow use case, which is to create or splice in an entry to give
++	 * access to a snapshot.
++	 *
++	 * We need to set the op flags directly. We hardcode
++	 * DCACHE_OP_REVALIDATE because that's the only operation we have; if
++	 * we ever extend zpl_dops_snapdirs we will need to update the op flags
++	 * to match.
++	 */
++	spin_lock(&dentry->d_lock);
++	dentry->d_op = &zpl_dops_snapdirs;
++	dentry->d_flags &= ~op_flags;
++	dentry->d_flags |= DCACHE_OP_REVALIDATE | extraflags;
++	spin_unlock(&dentry->d_lock);
++#endif
++}
++
+ static struct dentry *
+ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
+     unsigned int flags)
+@@ -236,10 +281,7 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
+ 		return (ERR_PTR(error));
+ 
+ 	ASSERT(error == 0 || ip == NULL);
+-	d_clear_d_op(dentry);
+-	d_set_d_op(dentry, &zpl_dops_snapdirs);
+-	dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
+-
++	set_snapdir_dentry_ops(dentry, DCACHE_NEED_AUTOMOUNT);
+ 	return (d_splice_alias(ip, dentry));
+ }
+ 
+@@ -373,8 +415,7 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
+ 
+ 	error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
+ 	if (error == 0) {
+-		d_clear_d_op(dentry);
+-		d_set_d_op(dentry, &zpl_dops_snapdirs);
++		set_snapdir_dentry_ops(dentry, 0);
+ 		d_instantiate(dentry, ip);
+ 	}
+ 
diff -pruN 2.3.4-1/debian/patches/ubuntu/4000-zsys-support.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/4000-zsys-support.patch
--- 2.3.4-1/debian/patches/ubuntu/4000-zsys-support.patch	2024-09-05 02:37:33.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/4000-zsys-support.patch	2025-09-04 19:31:05.000000000 +0000
@@ -7,203 +7,12 @@ Description: Support zsys systems
  snapshots.
 Author: Jean-Baptiste Lallement <jean.baptiste@ubuntu.com>
         Didier Roche <didrocks@ubuntu.com>
-Last-Update: 2019-06-06
-Index: zfs-linux-2.1.2/etc/systemd/system-generators/zfs-mount-generator.in
+        Heitor Alves de Siqueira <halves@ubuntu.com>
+Last-Update: 2025-01-20
+Index: zfs-linux/contrib/initramfs/scripts/zfs
 ===================================================================
---- zfs-linux-2.1.2.orig/etc/systemd/system-generators/zfs-mount-generator.in
-+++ zfs-linux-2.1.2/etc/systemd/system-generators/zfs-mount-generator.in
-@@ -30,6 +30,8 @@ FSLIST="@sysconfdir@/zfs/zfs-list.cache"
- [ -d "${FSLIST}" ] || exit 0
- [ "$(echo "${FSLIST}"/*)" = "${FSLIST}/*" ] && exit 0
- 
-+OLD_IFS=$IFS
-+
- do_fail() {
-   printf 'zfs-mount-generator: %s\n' "$*" > /dev/kmsg
-   exit 1
-@@ -138,6 +140,9 @@ process_line() {
-     fi
-   done
- 
-+  # Escape the mountpoint per systemd policy.
-+  mountfile="$(systemd-escape --path --suffix=mount "${p_mountpoint}")"
-+
-   if [ -n "${p_systemd_after}" ] && \
-       [ "${p_systemd_after}" != "-" ] ; then
-     after="${p_systemd_after} ${after}"
-@@ -163,6 +168,62 @@ process_line() {
-       [ "${p_encroot}" != "-" ] ; then
-     keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
-     if [ "${p_encroot}" = "${dataset}" ] ; then
-+
-+      # Automount and unmount ZSys USERDATA datasets with keystore
-+      zsys_automount=0
-+      automount_loadkey_extra_args=""
-+      automount_pool=""
-+      automount_user=""
-+      if echo "${dataset}" | grep -q '/USERDATA/'; then
-+        automount_pool=${dataset%%/*}
-+        automount_user=${dataset##*/}
-+        # Only operate on user dataset mountpoint itself and not its children
-+        if ! echo "${automount_user}" | grep -q '/'; then
-+          automount_user=${automount_user%%_*}
-+          # Ensure we have a keystore
-+          if [ -f "/run/keystore/${automount_pool}/${automount_user}.enc" -a -x /usr/sbin/user_keystore ]; then
-+            zsys_automount=1
-+          fi
-+        fi
-+      fi
-+
-+      # Create automount unit and keystore tracker
-+      if [ ${zsys_automount} -eq 1 ]; then
-+        automountunit="$(systemd-escape --path --suffix=automount "${p_mountpoint}")"
-+      echo \
-+"# Automatically generated by zfs-mount-generator
-+
-+[Unit]
-+Description=Automount ZFS user home for ${dataset} on demand
-+
-+[Automount]
-+Where=${p_mountpoint}
-+TimeoutIdleSec=10
-+
-+[Install]
-+WantedBy=local-fs.target
-+"   > "${dest_norm}/${automountunit}"
-+        create_dependencies "${automountunit}" "wants" "local-fs.target"
-+
-+        keystoreunit="zfs-keystore-$(systemd-escape "${p_encroot}").service"
-+        automount_loadkey_extra_args="BindsTo=${mountfile}
-+BindsTo=${keystoreunit}
-+After=${keystoreunit}"
-+        echo \
-+"# Automatically generated by zfs-mount-generator
-+
-+[Unit]
-+Description=Make available ZFS encryption key for ${dataset} from keystore
-+ConditionPathExists=/run/keystore/${automount_pool}/${automount_user}.enc
-+BindsTo=${keyloadunit}
-+
-+[Service]
-+Type=oneshot
-+RemainAfterExit=yes
-+ExecStop=/usr/sbin/user_keystore lock ${automount_pool} ${automount_user}
-+"   > "${dest_norm}/${keystoreunit}"
-+      fi
-+
-       keymountdep=""
-       if [ "${p_keyloc%%://*}" = "file" ] ; then
-         if [ -n "${requiredmounts}" ] ; then
-@@ -218,6 +279,7 @@ Wants=${wants}
- After=${after}
- ${requires}
- ${keymountdep}
-+${automount_loadkey_extra_args}
- 
- [Service]
- Type=oneshot
-@@ -270,9 +332,6 @@ ExecStop=${keyunloadcmd}"   > "${dest_no
-     do_fail "invalid mountpoint for ${dataset}"
-   fi
- 
--  # Escape the mountpoint per systemd policy.
--  mountfile="$(systemd-escape --path --suffix=mount "${p_mountpoint}")"
--
-   # Parse options
-   # see lib/libzfs/libzfs_mount.c:zfs_add_options
-   opts=""
-@@ -459,6 +518,87 @@ Options=defaults${opts},zfsutil" > "${de
- 
- }
- 
-+ZPOOL_CACHE="@sysconfdir@/zfs/zpool.cache"
-+PROPS="name,mountpoint,canmount,atime,relatime,devices,exec\
-+,readonly,setuid,nbmand,encroot,keylocation\
-+,org.openzfs.systemd:requires,org.openzfs.systemd:requires-mounts-for\
-+,org.openzfs.systemd:before,org.openzfs.systemd:after\
-+,org.openzfs.systemd:wanted-by,org.openzfs.systemd:required-by\
-+,org.openzfs.systemd:nofail,org.openzfs.systemd:ignore"
-+zsys_revert_failed=0
-+errfile="/tmp/zsys-revert-out.log"
-+
-+drop_emergency_on_failure() {
-+  if [ ${zsys_revert_failed} -eq 0 ]; then
-+    return
-+  fi
-+
-+  # Drop to emergency target in case of failure after cleanup fstab mountpoints.
-+  # This avoids booting and having a mix of old and new datasets, and creating directory in the wrong
-+  # datasets, like /boot/grub in / which will prevent zfs to mount /boot dataset later on.
-+  rm -f "${dest_norm}"/*.mount
-+  ln -s /lib/systemd/system/emergency.target "${dest_norm}"/default.target
-+
-+  printf 'ERROR: zfs-mount-generator failed and you requested a revert:\n' > /dev/kmsg
-+  cat "${errfile}" > /dev/kmsg
-+  printf 'You can reboot on current master dataset to fix the issue\n' > /dev/kmsg
-+}
-+
-+# Handle revert so that zsys prepares all datasets as expected.
-+initzsys() {
-+  if [ ! -x @sbindir@/zsysd ]; then
-+    return
-+  fi
-+
-+  # Non ZFS system
-+  if ! grep -q "root=ZFS=" /proc/cmdline; then
-+    return
-+  fi
-+
-+  # If we boot on the same dataset than last time, assume we don’t need to do anything as the cache file will only
-+  # import desired pools.
-+  bootds="$(sed -e 's/.*root=ZFS=\([^ ]\+\).*/\1/' /proc/cmdline)"
-+  if grep -Eq "${bootds}\s+/\s+on" "${FSLIST}/"*; then
-+      return
-+  fi
-+
-+  # If we get here: we are reverting. Let zsys handle it
-+  trap drop_emergency_on_failure EXIT INT QUIT ABRT PIPE TERM
-+
-+  exec 3>&1 1>"${errfile}"
-+  exec 4>&2 2>&1
-+
-+  zsys_revert_failed=1
-+  # Import and list previously imported pools for zsys
-+  if [ -f "${ZPOOL_CACHE}" ]; then
-+    @sbindir@/zpool import -c "${ZPOOL_CACHE}" -aN
-+  # As a best effort, import all available pools, hoping there is no conflict.
-+  else
-+    echo "We had to search for all available pools because ${ZPOOL_CACHE} doesn't exist. To avoid this, create a zpool cache file."
-+    @sbindir@/zpool import -aN
-+  fi
-+
-+  @sbindir@/zsysd boot-prepare >"${errfile}"
-+
-+  # If FSLIST is empty, populate with all imported pools
-+  if [ -z "$(ls -A ${FSLIST})" ]; then
-+    @sbindir@/zpool list -H -o name | xargs -I{} touch ${FSLIST}/{}
-+  fi
-+
-+  # Refresh zfs list cache
-+  for cachefile in "${FSLIST}/"* ; do
-+    pool=`basename ${cachefile}`
-+    @sbindir@/zfs list -H -t filesystem -o "${PROPS}" -r "${pool}" >"${cachefile}"
-+  done
-+
-+  exec 1>&3 3>&-
-+  exec 2>&4 4>&-
-+  zsys_revert_failed=0
-+  rm "${errfile}"
-+}
-+
-+initzsys
-+
- for cachefile in "${FSLIST}/"* ; do
-   # Disable glob expansion to protect against special characters when parsing.
-   set -f
-Index: zfs-linux-2.1.2/contrib/initramfs/scripts/zfs
-===================================================================
---- zfs-linux-2.1.2.orig/contrib/initramfs/scripts/zfs
-+++ zfs-linux-2.1.2/contrib/initramfs/scripts/zfs
+--- zfs-linux.orig/contrib/initramfs/scripts/zfs
++++ zfs-linux/contrib/initramfs/scripts/zfs
 @@ -66,6 +66,20 @@ get_fs_value()
  	"${ZFS}" get -H -ovalue "$value" "$fs" 2> /dev/null
  }
@@ -225,7 +34,7 @@ Index: zfs-linux-2.1.2/contrib/initramfs
  # Find the 'bootfs' property on pool $1.
  # If the property does not contain '/', then ignore this
  # pool by exporting it again.
-@@ -483,16 +497,17 @@ clone_snap()
+@@ -505,16 +519,17 @@ clone_snap()
  	snap="$1"
  	destfs="$2"
  	mountpoint="$3"
@@ -249,7 +58,7 @@ Index: zfs-linux-2.1.2/contrib/initramfs
  	ZFS_CMD="${ZFS_CMD} $snap $destfs"
  	ZFS_STDERR="$(${ZFS_CMD} 2>&1)"
  	ZFS_ERROR="$?"
-@@ -611,6 +626,15 @@ setup_snapshot_booting()
+@@ -633,6 +648,15 @@ setup_snapshot_booting()
  	snapname="${snap##*@}"
  	ZFS_BOOTFS="${rootfs}_${snapname}"
  
@@ -265,7 +74,7 @@ Index: zfs-linux-2.1.2/contrib/initramfs
  	if ! grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline
  	then
  		# If the destination dataset for the clone
-@@ -640,10 +664,18 @@ setup_snapshot_booting()
+@@ -664,10 +688,18 @@ setup_snapshot_booting()
  			#       rpool/ROOT/debian/boot@snap2	=> rpool/ROOT/debian_snap2/boot
  			#       rpool/ROOT/debian/usr@snap2	=> rpool/ROOT/debian_snap2/usr
  			#       rpool/ROOT/debian/var@snap2	=> rpool/ROOT/debian_snap2/var
@@ -274,8 +83,8 @@ Index: zfs-linux-2.1.2/contrib/initramfs
 +			#       rpool/ROOT/debian_uid1@snap2		=> rpool/ROOT/debian_uid2
 +			#       rpool/ROOT/debian_uid1/boot@snap2	=> rpool/ROOT/debian_uid2/boot
 +
- 			subfs="${s##$rootfs}"
- 			subfs="${subfs%%@$snapname}"
+ 			subfs="${s##"$rootfs"}"
+ 			subfs="${subfs%%@"$snapname"}"
  
  			destfs="${rootfs}_${snapname}" # base fs.
 +			if [ "${use_zsys}" = "yes" ]; then
@@ -284,7 +93,7 @@ Index: zfs-linux-2.1.2/contrib/initramfs
  			[ -n "$subfs" ] && destfs="${destfs}$subfs" # + sub fs.
  
  			# Get the mountpoint of the filesystem, to be used
-@@ -660,9 +692,38 @@ setup_snapshot_booting()
+@@ -684,9 +716,38 @@ setup_snapshot_booting()
  				fi
  			fi
  
@@ -324,7 +133,7 @@ Index: zfs-linux-2.1.2/contrib/initramfs
  			    retval=$((retval + 1))
  		fi
  	done
-@@ -887,6 +948,38 @@ mountroot()
+@@ -911,6 +972,38 @@ mountroot()
  		shell
  	fi
  
@@ -361,9 +170,9 @@ Index: zfs-linux-2.1.2/contrib/initramfs
 +	fi
 +
  	# In case the pool was specified as guid, resolve guid to name
- 	pool="$("${ZPOOL}" get name,guid -o name,value -H | \
+ 	pool="$("${ZPOOL}" get -H -o name,value name,guid | \
  	    awk -v pool="${ZFS_RPOOL}" '$2 == pool { print $1 }')"
-@@ -906,6 +999,8 @@ mountroot()
+@@ -930,6 +1023,8 @@ mountroot()
  		# Booting from a snapshot?
  		# Will overwrite the ZFS_BOOTFS variable like so:
  		#   rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2
@@ -372,7 +181,7 @@ Index: zfs-linux-2.1.2/contrib/initramfs
  		echo "${ZFS_BOOTFS}" | grep -q '@' && \
  		    setup_snapshot_booting "${ZFS_BOOTFS}"
  	fi
-@@ -943,13 +1038,23 @@ mountroot()
+@@ -967,13 +1062,23 @@ mountroot()
  	# Go through the complete list (recursively) of all filesystems below
  	# the real root dataset
  	filesystems="$("${ZFS}" list -oname -tfilesystem -H -r "${ZFS_BOOTFS}")"
@@ -396,7 +205,7 @@ Index: zfs-linux-2.1.2/contrib/initramfs
  		mount_fs "$fs"
  	done
  
-@@ -995,3 +1100,8 @@ mountroot()
+@@ -1019,3 +1124,8 @@ mountroot()
  		fi
  	fi
  }
@@ -405,3 +214,195 @@ Index: zfs-linux-2.1.2/contrib/initramfs
 +{
 +	grep -a -m10 -E "\*" /dev/urandom 2>/dev/null | tr -dc 'a-z0-9' | cut -c-6
 +}
+Index: zfs-linux/etc/systemd/system-generators/zfs-mount-generator.in
+===================================================================
+--- zfs-linux.orig/etc/systemd/system-generators/zfs-mount-generator.in
++++ zfs-linux/etc/systemd/system-generators/zfs-mount-generator.in
+@@ -30,6 +30,8 @@ FSLIST="@sysconfdir@/zfs/zfs-list.cache"
+ [ -d "${FSLIST}" ] || exit 0
+ [ "$(echo "${FSLIST}"/*)" = "${FSLIST}/*" ] && exit 0
+ 
++OLD_IFS=$IFS
++
+ do_fail() {
+   printf 'zfs-mount-generator: %s\n' "$*" > /dev/kmsg
+   exit 1
+@@ -138,6 +140,9 @@ process_line() {
+     fi
+   done
+ 
++  # Escape the mountpoint per systemd policy.
++  mountfile="$(systemd-escape --path --suffix=mount "${p_mountpoint}")"
++
+   if [ -n "${p_systemd_after}" ] && \
+       [ "${p_systemd_after}" != "-" ] ; then
+     after="${p_systemd_after} ${after}"
+@@ -163,6 +168,62 @@ process_line() {
+       [ "${p_encroot}" != "-" ] ; then
+     keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
+     if [ "${p_encroot}" = "${dataset}" ] ; then
++
++      # Automount and unmount ZSys USERDATA datasets with keystore
++      zsys_automount=0
++      automount_loadkey_extra_args=""
++      automount_pool=""
++      automount_user=""
++      if echo "${dataset}" | grep -q '/USERDATA/'; then
++        automount_pool=${dataset%%/*}
++        automount_user=${dataset##*/}
++        # Only operate on user dataset mountpoint itself and not its children
++        if ! echo "${automount_user}" | grep -q '/'; then
++          automount_user=${automount_user%%_*}
++          # Ensure we have a keystore
++          if [ -f "/run/keystore/${automount_pool}/${automount_user}.enc" -a -x /usr/sbin/user_keystore ]; then
++            zsys_automount=1
++          fi
++        fi
++      fi
++
++      # Create automount unit and keystore tracker
++      if [ ${zsys_automount} -eq 1 ]; then
++        automountunit="$(systemd-escape --path --suffix=automount "${p_mountpoint}")"
++      echo \
++"# Automatically generated by zfs-mount-generator
++
++[Unit]
++Description=Automount ZFS user home for ${dataset} on demand
++
++[Automount]
++Where=${p_mountpoint}
++TimeoutIdleSec=10
++
++[Install]
++WantedBy=local-fs.target
++"   > "${dest_norm}/${automountunit}"
++        create_dependencies "${automountunit}" "wants" "local-fs.target"
++
++        keystoreunit="zfs-keystore-$(systemd-escape "${p_encroot}").service"
++        automount_loadkey_extra_args="BindsTo=${mountfile}
++BindsTo=${keystoreunit}
++After=${keystoreunit}"
++        echo \
++"# Automatically generated by zfs-mount-generator
++
++[Unit]
++Description=Make available ZFS encryption key for ${dataset} from keystore
++ConditionPathExists=/run/keystore/${automount_pool}/${automount_user}.enc
++BindsTo=${keyloadunit}
++
++[Service]
++Type=oneshot
++RemainAfterExit=yes
++ExecStop=/usr/sbin/user_keystore lock ${automount_pool} ${automount_user}
++"   > "${dest_norm}/${keystoreunit}"
++      fi
++
+       keymountdep=""
+       if [ "${p_keyloc%%://*}" = "file" ] ; then
+         if [ -n "${requiredmounts}" ] ; then
+@@ -218,6 +279,7 @@ Wants=${wants}
+ After=${after}
+ ${requires}
+ ${keymountdep}
++${automount_loadkey_extra_args}
+ 
+ [Service]
+ Type=oneshot
+@@ -270,9 +332,6 @@ ExecStop=${keyunloadcmd}"   > "${dest_no
+     do_fail "invalid mountpoint for ${dataset}"
+   fi
+ 
+-  # Escape the mountpoint per systemd policy.
+-  mountfile="$(systemd-escape --path --suffix=mount "${p_mountpoint}")"
+-
+   # Parse options
+   # see lib/libzfs/libzfs_mount.c:zfs_add_options
+   opts=""
+@@ -459,6 +518,87 @@ Options=defaults${opts},zfsutil" > "${de
+ 
+ }
+ 
++ZPOOL_CACHE="@sysconfdir@/zfs/zpool.cache"
++PROPS="name,mountpoint,canmount,atime,relatime,devices,exec\
++,readonly,setuid,nbmand,encroot,keylocation\
++,org.openzfs.systemd:requires,org.openzfs.systemd:requires-mounts-for\
++,org.openzfs.systemd:before,org.openzfs.systemd:after\
++,org.openzfs.systemd:wanted-by,org.openzfs.systemd:required-by\
++,org.openzfs.systemd:nofail,org.openzfs.systemd:ignore"
++zsys_revert_failed=0
++errfile="/tmp/zsys-revert-out.log"
++
++drop_emergency_on_failure() {
++  if [ ${zsys_revert_failed} -eq 0 ]; then
++    return
++  fi
++
++  # Drop to emergency target in case of failure after cleanup fstab mountpoints.
++  # This avoids booting and having a mix of old and new datasets, and creating directory in the wrong
++  # datasets, like /boot/grub in / which will prevent zfs to mount /boot dataset later on.
++  rm -f "${dest_norm}"/*.mount
++  ln -s /lib/systemd/system/emergency.target "${dest_norm}"/default.target
++
++  printf 'ERROR: zfs-mount-generator failed and you requested a revert:\n' > /dev/kmsg
++  cat "${errfile}" > /dev/kmsg
++  printf 'You can reboot on current master dataset to fix the issue\n' > /dev/kmsg
++}
++
++# Handle revert so that zsys prepares all datasets as expected.
++initzsys() {
++  if [ ! -x @sbindir@/zsysd ]; then
++    return
++  fi
++
++  # Non ZFS system
++  if ! grep -q "root=ZFS=" /proc/cmdline; then
++    return
++  fi
++
++  # If we boot on the same dataset than last time, assume we don’t need to do anything as the cache file will only
++  # import desired pools.
++  bootds="$(sed -e 's/.*root=ZFS=\([^ ]\+\).*/\1/' /proc/cmdline)"
++  if grep -Eq "${bootds}\s+/\s+on" "${FSLIST}/"*; then
++      return
++  fi
++
++  # If we get here: we are reverting. Let zsys handle it
++  trap drop_emergency_on_failure EXIT INT QUIT ABRT PIPE TERM
++
++  exec 3>&1 1>"${errfile}"
++  exec 4>&2 2>&1
++
++  zsys_revert_failed=1
++  # Import and list previously imported pools for zsys
++  if [ -f "${ZPOOL_CACHE}" ]; then
++    @sbindir@/zpool import -c "${ZPOOL_CACHE}" -aN
++  # As a best effort, import all available pools, hoping there is no conflict.
++  else
++    echo "We had to search for all available pools because ${ZPOOL_CACHE} doesn't exist. To avoid this, create a zpool cache file."
++    @sbindir@/zpool import -aN
++  fi
++
++  @sbindir@/zsysd boot-prepare >"${errfile}"
++
++  # If FSLIST is empty, populate with all imported pools
++  if [ -z "$(ls -A ${FSLIST})" ]; then
++    @sbindir@/zpool list -H -o name | xargs -I{} touch ${FSLIST}/{}
++  fi
++
++  # Refresh zfs list cache
++  for cachefile in "${FSLIST}/"* ; do
++    pool=`basename ${cachefile}`
++    @sbindir@/zfs list -H -t filesystem -o "${PROPS}" -r "${pool}" >"${cachefile}"
++  done
++
++  exec 1>&3 3>&-
++  exec 2>&4 4>&-
++  zsys_revert_failed=0
++  rm "${errfile}"
++}
++
++initzsys
++
+ for cachefile in "${FSLIST}/"* ; do
+   # Disable glob expansion to protect against special characters when parsing.
+   set -f
diff -pruN 2.3.4-1/debian/patches/ubuntu/4001-dracut-Open-and-mount-luks-keystore.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/4001-dracut-Open-and-mount-luks-keystore.patch
--- 2.3.4-1/debian/patches/ubuntu/4001-dracut-Open-and-mount-luks-keystore.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/4001-dracut-Open-and-mount-luks-keystore.patch	2025-09-04 19:30:52.000000000 +0000
@@ -0,0 +1,68 @@
+From: Benjamin Drung <benjamin.drung@canonical.com>
+Date: Thu, 4 Sep 2025 21:11:51 +0200
+Subject: dracut: Open and mount luks keystore
+
+Booting an encrypted ZFS system with dracut fails:
+
+```
+dracut-pre-mount[817]: Warning: ZFS: Key /run/keystore/rpool/system.key for rpool hasn't appeared. Trying anyway.
+dracut-pre-mount[863]: Key load error: Failed to open key material file: No such file or directory
+[FAILED] Failed to mount sysroot.mount - /sysroot.
+```
+
+4000-zsys-support.patch enhances `contrib/initramfs/scripts/zfs` to open
+and mount luks keystore for any pools using one. Port this Ubuntu
+keystore convention to Dracut.
+
+Bug-Ubuntu: https://launchpad.net/bugs/2070066
+---
+ contrib/dracut/90zfs/zfs-load-key.sh.in | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/contrib/dracut/90zfs/zfs-load-key.sh.in b/contrib/dracut/90zfs/zfs-load-key.sh.in
+index 8e68468..1932100 100755
+--- a/contrib/dracut/90zfs/zfs-load-key.sh.in
++++ b/contrib/dracut/90zfs/zfs-load-key.sh.in
+@@ -22,6 +22,29 @@ fi
+ 
+ [ "$(zpool get -Ho value feature@encryption "${BOOTFS%%/*}")" = 'active' ] || return 0
+ 
++_open_and_mount_luks_keystore() {
++    pool="$1"
++    keyfile="$2"
++
++    ks="/dev/zvol/$pool/keystore"
++    if [ ! -e "$ks" ]; then
++        echo "Error: $ks does not exist." >&2
++        return 1
++    fi
++
++    systemd-cryptsetup attach "keystore-${pool}" "${ks}"
++
++    dev="/dev/mapper/keystore-${pool}"
++    if [ ! -e "$dev" ]; then
++        echo "Error: $dev does not exist." >&2
++        return 1
++    fi
++
++    keypath="${keyfile%/*}"
++    mkdir -p "${keypath}"
++    mount -o discard "${dev}" "${keypath}"
++}
++
+ _load_key_cb() {
+     dataset="$1"
+ 
+@@ -31,6 +54,12 @@ _load_key_cb() {
+     [ "$(zfs get -Ho value keystatus "${ENCRYPTIONROOT}")" = "unavailable" ] || return 0
+ 
+     KEYLOCATION="$(zfs get -Ho value keylocation "${ENCRYPTIONROOT}")"
++    case "$KEYLOCATION" in
++        "file:///run/keystore/${ENCRYPTIONROOT}/"*)
++            _open_and_mount_luks_keystore "${ENCRYPTIONROOT}" "${KEYLOCATION#file://}"
++            ;;
++    esac
++
+     case "${KEYLOCATION%%://*}" in
+         prompt)
+             for _ in 1 2 3; do
diff -pruN 2.3.4-1/debian/patches/ubuntu/4510-silently-ignore-modprobe-failure.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/4510-silently-ignore-modprobe-failure.patch
--- 2.3.4-1/debian/patches/ubuntu/4510-silently-ignore-modprobe-failure.patch	2024-09-05 02:37:33.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/4510-silently-ignore-modprobe-failure.patch	2025-09-04 19:31:05.000000000 +0000
@@ -5,29 +5,25 @@ Origin: ubuntu
 Forwarded: no
 Last-Update: 2020-06-04
 
-Index: zfs-linux-2.0.1/etc/systemd/system/zfs-load-module.service.in
-===================================================================
---- zfs-linux-2.0.1.orig/etc/systemd/system/zfs-load-module.service.in
-+++ zfs-linux-2.0.1/etc/systemd/system/zfs-load-module.service.in
-@@ -10,7 +10,7 @@ After=systemd-remount-fs.service
+--- a/etc/systemd/system/zfs-load-module.service.in
++++ b/etc/systemd/system/zfs-load-module.service.in
+@@ -10,7 +10,7 @@
  [Service]
  Type=oneshot
  RemainAfterExit=yes
--ExecStart=/sbin/modprobe zfs
-+ExecStart=-/sbin/modprobe zfs
+-ExecStart=/usr/sbin/modprobe zfs
++ExecStart=-/usr/sbin/modprobe zfs
  
  [Install]
  WantedBy=zfs-mount.service
-Index: zfs-linux-2.0.1/etc/systemd/system/zfs-share.service.in
-===================================================================
---- zfs-linux-2.0.1.orig/etc/systemd/system/zfs-share.service.in
-+++ zfs-linux-2.0.1/etc/systemd/system/zfs-share.service.in
-@@ -13,7 +13,7 @@ ConditionPathIsDirectory=/sys/module/zfs
- [Service]
+--- a/etc/systemd/system/zfs-share.service.in
++++ b/etc/systemd/system/zfs-share.service.in
+@@ -14,7 +14,7 @@
  Type=oneshot
  RemainAfterExit=yes
--ExecStart=@sbindir@/zfs share -a
-+ExecStart=-@sbindir@/zfs share -a
+ EnvironmentFile=-@initconfdir@/zfs
+-ExecStart=zfs share -a
++ExecStart=-zfs share -a
  
  [Install]
  WantedBy=zfs.target
diff -pruN 2.3.4-1/debian/patches/ubuntu/fixup-abi.patch 2.3.4-1ubuntu2/debian/patches/ubuntu/fixup-abi.patch
--- 2.3.4-1/debian/patches/ubuntu/fixup-abi.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/patches/ubuntu/fixup-abi.patch	2025-09-04 19:31:05.000000000 +0000
@@ -0,0 +1,38 @@
+Description: fixup abi check files
+Author: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+Bug: https://github.com/openzfs/zfs/issues/15196
+
+
+--- zfs-linux-2.2.0~rc3.orig/lib/libuutil/libuutil.abi
++++ zfs-linux-2.2.0~rc3/lib/libuutil/libuutil.abi
+@@ -176,8 +176,6 @@
+     <elf-symbol name='mkdirp' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='print_timestamp' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='spl_pagesize' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+-    <elf-symbol name='strlcat' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+-    <elf-symbol name='strlcpy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='uu_avl_create' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='uu_avl_destroy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='uu_avl_find' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+--- zfs-linux-2.2.0~rc3.orig/lib/libzfs/libzfs.abi
++++ zfs-linux-2.2.0~rc3/lib/libzfs/libzfs.abi
+@@ -248,8 +248,6 @@
+     <elf-symbol name='sa_validate_shareopts' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='snapshot_namecheck' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='spl_pagesize' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+-    <elf-symbol name='strlcat' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+-    <elf-symbol name='strlcpy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='tpool_abandon' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='tpool_create' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='tpool_destroy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+--- zfs-linux-2.2.0~rc3.orig/lib/libzfs_core/libzfs_core.abi
++++ zfs-linux-2.2.0~rc3/lib/libzfs_core/libzfs_core.abi
+@@ -213,8 +213,6 @@
+     <elf-symbol name='mkdirp' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='print_timestamp' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+     <elf-symbol name='spl_pagesize' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+-    <elf-symbol name='strlcat' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+-    <elf-symbol name='strlcpy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+   </elf-function-symbols>
+   <abi-instr address-size='64' path='lib/libspl/assert.c' language='LANG_C99'>
+     <class-decl name='__va_list_tag' size-in-bits='192' is-struct='yes' visibility='default' id='d5027220'>
diff -pruN 2.3.4-1/debian/rules 2.3.4-1ubuntu2/debian/rules
--- 2.3.4-1/debian/rules	2025-08-26 16:26:57.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/rules	2025-09-04 08:22:32.000000000 +0000
@@ -81,7 +81,7 @@ override_dh_auto_build:
 override_dh_auto_test:
 ifeq (amd64,$(DEB_HOST_ARCH))
 	# Upstream provides an ABI guarantee that we validate here
-	-$(MAKE) checkabi
+	$(MAKE) checkabi
 endif
 
 	# The dh_auto_test rule is disabled because
diff -pruN 2.3.4-1/debian/tests/control 2.3.4-1ubuntu2/debian/tests/control
--- 2.3.4-1/debian/tests/control	2025-03-18 12:24:04.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/tests/control	2025-09-04 08:22:32.000000000 +0000
@@ -1,46 +1,8 @@
-Tests: kernel-smoke-test, kernel-ztest
+Tests: kernel-smoke-test
 Architecture: !i386
-Restrictions: needs-root, allow-stderr, isolation-machine
+Restrictions: needs-root, allow-stderr, isolation-machine, skippable
 Depends: zfs-dkms [ amd64 arm64 ppc64el s390x ],
          zfs-initramfs [ amd64 arm64 ppc64el s390x ],
          zfs-test [ amd64 arm64 ppc64el s390x ],
          zfs-zed [ amd64 arm64 ppc64el s390x ],
          zfsutils-linux [ amd64 arm64 ppc64el s390x ],
-         linux-headers-amd64 [amd64],
-         linux-headers-arm64 [arm64],
-         linux-headers-armmp [armhf],
-         linux-headers-rpi [armel],
-         linux-headers-powerpc64le [ppc64el],
-         linux-headers-s390x [s390x],
-         linux-headers-riscv64 [riscv64],
-         @recommends@
-
-Tests: zfs-test-suite-1, zfs-test-suite-2, zfs-test-suite-3, zfs-test-suite-4
-Architecture: !i386
-Restrictions: needs-root, allow-stderr, isolation-machine, breaks-testbed, flaky-and-slow
-Depends: zfs-dkms [ amd64 arm64 ppc64el s390x ],
-         zfs-initramfs [ amd64 arm64 ppc64el s390x ],
-         zfs-test [ amd64 arm64 ppc64el s390x ],
-         zfs-zed [ amd64 arm64 ppc64el s390x ],
-         zfsutils-linux [ amd64 arm64 ppc64el s390x ],
-         linux-headers-amd64 [amd64],
-         linux-headers-arm64 [arm64],
-         linux-headers-armmp [armhf],
-         linux-headers-rpi [armel],
-         linux-headers-powerpc64le [ppc64el],
-         linux-headers-s390x [s390x],
-         linux-headers-riscv64 [riscv64],
-         @recommends@
-
-Tests: binary-debs-modules, binary-debs-modules-udeb
-Architecture: !i386
-Restrictions: needs-root, allow-stderr
-Depends: fakeroot,
-         linux-headers-amd64 [amd64],
-         linux-headers-arm64 [arm64],
-         linux-headers-armmp [armhf],
-         linux-headers-rpi [armel],
-         linux-headers-powerpc64le [ppc64el],
-         linux-headers-s390x [s390x],
-         linux-headers-riscv64 [riscv64],
-         @builddeps@
diff -pruN 2.3.4-1/debian/tests/kernel-smoke-test-pool-draid 2.3.4-1ubuntu2/debian/tests/kernel-smoke-test-pool-draid
--- 2.3.4-1/debian/tests/kernel-smoke-test-pool-draid	2024-11-10 06:13:15.000000000 +0000
+++ 2.3.4-1ubuntu2/debian/tests/kernel-smoke-test-pool-draid	2025-09-04 08:22:32.000000000 +0000
@@ -2,6 +2,11 @@
 
 set -e
 
+if [ "$(uname -i)" = "s390x" ]; then
+       echo "skip draid tests that are expected to fail on s390x arch - LP: #2097378"
+       exit 0
+fi
+
 ./debian/tests/kernel-smoke-test-pool "draid1"
 ./debian/tests/kernel-smoke-test-pool "draid2"
 ./debian/tests/kernel-smoke-test-pool "draid3"
