zfsonlinux/debian/patches/ubuntu/0003-Revert-etc-systemd-zfs-mount-generator-rewrite-in-C.patch
2025-01-15 16:52:43 +08:00

1635 lines
48 KiB
Diff

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