0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-13 21:50:33 +03:00

nixos/zfs: remove top-level withs

As it is generally considered an anti-pattern. Removing them here in
fact exposed one bug (see previous commit).
This commit is contained in:
Andrew Marshall 2024-09-24 14:21:39 -04:00
parent 06ed2c46fb
commit 33bd85a6c8

View file

@ -2,9 +2,6 @@
# #
# TODO: zfs tunables # TODO: zfs tunables
with utils;
with lib;
let let
cfgZfs = config.boot.zfs; cfgZfs = config.boot.zfs;
@ -17,7 +14,7 @@ let
cfgZED = config.services.zfs.zed; cfgZED = config.services.zfs.zed;
selectModulePackage = package: config.boot.kernelPackages.${package.kernelModuleAttribute}; selectModulePackage = package: config.boot.kernelPackages.${package.kernelModuleAttribute};
clevisDatasets = attrNames (filterAttrs (device: _: any (e: e.fsType == "zfs" && (fsNeededForBoot e) && (e.device == device || hasPrefix "${device}/" e.device)) config.system.build.fileSystems) config.boot.initrd.clevis.devices); clevisDatasets = lib.attrNames (lib.filterAttrs (device: _: lib.any (e: e.fsType == "zfs" && (utils.fsNeededForBoot e) && (e.device == device || lib.hasPrefix "${device}/" e.device)) config.system.build.fileSystems) config.boot.initrd.clevis.devices);
inInitrd = config.boot.initrd.supportedFilesystems.zfs or false; inInitrd = config.boot.initrd.supportedFilesystems.zfs or false;
inSystem = config.boot.supportedFilesystems.zfs or false; inSystem = config.boot.supportedFilesystems.zfs or false;
@ -28,17 +25,17 @@ let
zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot"; zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot";
datasetToPool = x: elemAt (splitString "/" x) 0; datasetToPool = x: lib.elemAt (lib.splitString "/" x) 0;
fsToPool = fs: datasetToPool fs.device; fsToPool = fs: datasetToPool fs.device;
zfsFilesystems = filter (x: x.fsType == "zfs") config.system.build.fileSystems; zfsFilesystems = lib.filter (x: x.fsType == "zfs") config.system.build.fileSystems;
allPools = unique ((map fsToPool zfsFilesystems) ++ cfgZfs.extraPools); allPools = lib.unique ((map fsToPool zfsFilesystems) ++ cfgZfs.extraPools);
rootPools = unique (map fsToPool (filter fsNeededForBoot zfsFilesystems)); rootPools = lib.unique (map fsToPool (lib.filter utils.fsNeededForBoot zfsFilesystems));
dataPools = unique (filter (pool: !(elem pool rootPools)) allPools); dataPools = lib.unique (lib.filter (pool: !(lib.elem pool rootPools)) allPools);
snapshotNames = [ "frequent" "hourly" "daily" "weekly" "monthly" ]; snapshotNames = [ "frequent" "hourly" "daily" "weekly" "monthly" ];
@ -89,7 +86,7 @@ let
''; '';
getPoolFilesystems = pool: getPoolFilesystems = pool:
filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems; lib.filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
getPoolMounts = prefix: pool: getPoolMounts = prefix: pool:
let let
@ -98,36 +95,36 @@ let
# Remove the "/" suffix because even though most mountpoints # Remove the "/" suffix because even though most mountpoints
# won't have it, the "/" mountpoint will, and we can't have the # won't have it, the "/" mountpoint will, and we can't have the
# trailing slash in "/sysroot/" in stage 1. # trailing slash in "/sysroot/" in stage 1.
mountPoint = fs: escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint)); mountPoint = fs: utils.escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint));
hasUsr = lib.any (fs: fs.mountPoint == "/usr") poolFSes; hasUsr = lib.any (fs: fs.mountPoint == "/usr") poolFSes;
in in
map (x: "${mountPoint x}.mount") poolFSes map (x: "${mountPoint x}.mount") poolFSes
++ lib.optional hasUsr "sysusr-usr.mount"; ++ lib.optional hasUsr "sysusr-usr.mount";
getKeyLocations = pool: if isBool cfgZfs.requestEncryptionCredentials then { getKeyLocations = pool: if lib.isBool cfgZfs.requestEncryptionCredentials then {
hasKeys = cfgZfs.requestEncryptionCredentials; hasKeys = cfgZfs.requestEncryptionCredentials;
command = "${cfgZfs.package}/sbin/zfs list -rHo name,keylocation,keystatus -t volume,filesystem ${pool}"; command = "${cfgZfs.package}/sbin/zfs list -rHo name,keylocation,keystatus -t volume,filesystem ${pool}";
} else let } else let
keys = filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials; keys = lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials;
in { in {
hasKeys = keys != []; hasKeys = keys != [];
command = "${cfgZfs.package}/sbin/zfs list -Ho name,keylocation,keystatus -t volume,filesystem ${toString keys}"; command = "${cfgZfs.package}/sbin/zfs list -Ho name,keylocation,keystatus -t volume,filesystem ${toString keys}";
}; };
createImportService = { pool, systemd, force, prefix ? "" }: createImportService = { pool, systemd, force, prefix ? "" }:
nameValuePair "zfs-import-${pool}" { lib.nameValuePair "zfs-import-${pool}" {
description = "Import ZFS pool \"${pool}\""; description = "Import ZFS pool \"${pool}\"";
# We wait for systemd-udev-settle to ensure devices are available, # We wait for systemd-udev-settle to ensure devices are available,
# but don't *require* it, because mounts shouldn't be killed if it's stopped. # but don't *require* it, because mounts shouldn't be killed if it's stopped.
# In the future, hopefully someone will complete this: # In the future, hopefully someone will complete this:
# https://github.com/zfsonlinux/zfs/pull/4943 # https://github.com/zfsonlinux/zfs/pull/4943
wants = [ "systemd-udev-settle.service" ] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target"; wants = [ "systemd-udev-settle.service" ] ++ lib.optional (config.boot.initrd.clevis.useTang) "network-online.target";
after = [ after = [
"systemd-udev-settle.service" "systemd-udev-settle.service"
"systemd-modules-load.service" "systemd-modules-load.service"
"systemd-ask-password-console.service" "systemd-ask-password-console.service"
] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target"; ] ++ lib.optional (config.boot.initrd.clevis.useTang) "network-online.target";
requiredBy = getPoolMounts prefix pool ++ [ "zfs-import.target" ]; requiredBy = getPoolMounts prefix pool ++ [ "zfs-import.target" ];
before = getPoolMounts prefix pool ++ [ "shutdown.target" "zfs-import.target" ]; before = getPoolMounts prefix pool ++ [ "shutdown.target" "zfs-import.target" ];
conflicts = [ "shutdown.target" ]; conflicts = [ "shutdown.target" ];
@ -138,7 +135,7 @@ let
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
}; };
environment.ZFS_FORCE = optionalString force "-f"; environment.ZFS_FORCE = lib.optionalString force "-f";
script = let script = let
keyLocations = getKeyLocations pool; keyLocations = getKeyLocations pool;
in (importLib { in (importLib {
@ -157,10 +154,10 @@ let
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool. poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
fi fi
if poolImported "${pool}"; then if poolImported "${pool}"; then
${optionalString config.boot.initrd.clevis.enable (concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem} || true ") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets))} ${lib.optionalString config.boot.initrd.clevis.enable (lib.concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem} || true ") (lib.filter (p: (lib.elemAt (lib.splitString "/" p) 0) == pool) clevisDatasets))}
${optionalString keyLocations.hasKeys '' ${lib.optionalString keyLocations.hasKeys ''
${keyLocations.command} | while IFS=$'\t' read ds kl ks; do ${keyLocations.command} | while IFS=$'\t' read ds kl ks; do
{ {
if [[ "$ks" != unavailable ]]; then if [[ "$ks" != unavailable ]]; then
@ -193,15 +190,15 @@ let
''; '';
}; };
zedConf = generators.toKeyValue { zedConf = lib.generators.toKeyValue {
mkKeyValue = generators.mkKeyValueDefault { mkKeyValue = lib.generators.mkKeyValueDefault {
mkValueString = v: mkValueString = v:
if isInt v then toString v if lib.isInt v then toString v
else if isString v then "\"${v}\"" else if lib.isString v then "\"${v}\""
else if true == v then "1" else if true == v then "1"
else if false == v then "0" else if false == v then "0"
else if isList v then "\"" + (concatStringsSep " " v) + "\"" else if lib.isList v then "\"" + (lib.concatStringsSep " " v) + "\""
else err "this value is" (toString v); else lib.err "this value is" (toString v);
} "="; } "=";
} cfgZED.settings; } cfgZED.settings;
in in
@ -209,38 +206,38 @@ in
{ {
imports = [ imports = [
(mkRemovedOptionModule [ "boot" "zfs" "enableLegacyCrypto" ] "The corresponding package was removed from nixpkgs.") (lib.mkRemovedOptionModule [ "boot" "zfs" "enableLegacyCrypto" ] "The corresponding package was removed from nixpkgs.")
(mkRemovedOptionModule [ "boot" "zfs" "enableUnstable" ] "Instead set `boot.zfs.package = pkgs.zfs_unstable;`") (lib.mkRemovedOptionModule [ "boot" "zfs" "enableUnstable" ] "Instead set `boot.zfs.package = pkgs.zfs_unstable;`")
]; ];
###### interface ###### interface
options = { options = {
boot.zfs = { boot.zfs = {
package = mkOption { package = lib.mkOption {
type = types.package; type = lib.types.package;
default = pkgs.zfs; default = pkgs.zfs;
defaultText = literalExpression "pkgs.zfs"; defaultText = lib.literalExpression "pkgs.zfs";
description = "Configured ZFS userland tools package, use `pkgs.zfs_unstable` if you want to track the latest staging ZFS branch."; description = "Configured ZFS userland tools package, use `pkgs.zfs_unstable` if you want to track the latest staging ZFS branch.";
}; };
modulePackage = mkOption { modulePackage = lib.mkOption {
internal = true; # It is supposed to be selected automatically, but can be overridden by expert users. internal = true; # It is supposed to be selected automatically, but can be overridden by expert users.
default = selectModulePackage cfgZfs.package; default = selectModulePackage cfgZfs.package;
type = types.package; type = lib.types.package;
description = "Configured ZFS kernel module package."; description = "Configured ZFS kernel module package.";
}; };
enabled = mkOption { enabled = lib.mkOption {
readOnly = true; readOnly = true;
type = types.bool; type = lib.types.bool;
default = inInitrd || inSystem; default = inInitrd || inSystem;
defaultText = literalMD "`true` if ZFS filesystem support is enabled"; defaultText = lib.literalMD "`true` if ZFS filesystem support is enabled";
description = "True if ZFS filesystem support is enabled"; description = "True if ZFS filesystem support is enabled";
}; };
allowHibernation = mkOption { allowHibernation = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = false; default = false;
description = '' description = ''
Allow hibernation support, this may be a unsafe option depending on your Allow hibernation support, this may be a unsafe option depending on your
@ -248,8 +245,8 @@ in
''; '';
}; };
extraPools = mkOption { extraPools = lib.mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = []; default = [];
example = [ "tank" "data" ]; example = [ "tank" "data" ];
description = '' description = ''
@ -267,8 +264,8 @@ in
''; '';
}; };
devNodes = mkOption { devNodes = lib.mkOption {
type = types.path; type = lib.types.path;
default = "/dev/disk/by-id"; default = "/dev/disk/by-id";
description = '' description = ''
Name of directory from which to import ZFS devices. Name of directory from which to import ZFS devices.
@ -278,8 +275,8 @@ in
''; '';
}; };
forceImportRoot = mkOption { forceImportRoot = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
description = '' description = ''
Forcibly import the ZFS root pool(s) during early boot. Forcibly import the ZFS root pool(s) during early boot.
@ -296,8 +293,8 @@ in
''; '';
}; };
forceImportAll = mkOption { forceImportAll = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = false; default = false;
description = '' description = ''
Forcibly import all ZFS pool(s). Forcibly import all ZFS pool(s).
@ -309,8 +306,8 @@ in
''; '';
}; };
requestEncryptionCredentials = mkOption { requestEncryptionCredentials = lib.mkOption {
type = types.either types.bool (types.listOf types.str); type = lib.types.either lib.types.bool (lib.types.listOf lib.types.str);
default = true; default = true;
example = [ "tank" "data" ]; example = [ "tank" "data" ];
description = '' description = ''
@ -321,8 +318,8 @@ in
''; '';
}; };
passwordTimeout = mkOption { passwordTimeout = lib.mkOption {
type = types.int; type = lib.types.int;
default = 0; default = 0;
description = '' description = ''
Timeout in seconds to wait for password entry for decrypt at boot. Timeout in seconds to wait for password entry for decrypt at boot.
@ -332,7 +329,7 @@ in
}; };
removeLinuxDRM = lib.mkOption { removeLinuxDRM = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = false; default = false;
description = '' description = ''
Patch the kernel to change symbols needed by ZFS from Patch the kernel to change symbols needed by ZFS from
@ -345,9 +342,9 @@ in
}; };
services.zfs.autoSnapshot = { services.zfs.autoSnapshot = {
enable = mkOption { enable = lib.mkOption {
default = false; default = false;
type = types.bool; type = lib.types.bool;
description = '' description = ''
Enable the (OpenSolaris-compatible) ZFS auto-snapshotting service. Enable the (OpenSolaris-compatible) ZFS auto-snapshotting service.
Note that you must set the `com.sun:auto-snapshot` Note that you must set the `com.sun:auto-snapshot`
@ -360,10 +357,10 @@ in
''; '';
}; };
flags = mkOption { flags = lib.mkOption {
default = "-k -p"; default = "-k -p";
example = "-k -p --utc"; example = "-k -p --utc";
type = types.str; type = lib.types.str;
description = '' description = ''
Flags to pass to the zfs-auto-snapshot command. Flags to pass to the zfs-auto-snapshot command.
@ -379,41 +376,41 @@ in
''; '';
}; };
frequent = mkOption { frequent = lib.mkOption {
default = 4; default = 4;
type = types.int; type = lib.types.int;
description = '' description = ''
Number of frequent (15-minute) auto-snapshots that you wish to keep. Number of frequent (15-minute) auto-snapshots that you wish to keep.
''; '';
}; };
hourly = mkOption { hourly = lib.mkOption {
default = 24; default = 24;
type = types.int; type = lib.types.int;
description = '' description = ''
Number of hourly auto-snapshots that you wish to keep. Number of hourly auto-snapshots that you wish to keep.
''; '';
}; };
daily = mkOption { daily = lib.mkOption {
default = 7; default = 7;
type = types.int; type = lib.types.int;
description = '' description = ''
Number of daily auto-snapshots that you wish to keep. Number of daily auto-snapshots that you wish to keep.
''; '';
}; };
weekly = mkOption { weekly = lib.mkOption {
default = 4; default = 4;
type = types.int; type = lib.types.int;
description = '' description = ''
Number of weekly auto-snapshots that you wish to keep. Number of weekly auto-snapshots that you wish to keep.
''; '';
}; };
monthly = mkOption { monthly = lib.mkOption {
default = 12; default = 12;
type = types.int; type = lib.types.int;
description = '' description = ''
Number of monthly auto-snapshots that you wish to keep. Number of monthly auto-snapshots that you wish to keep.
''; '';
@ -421,16 +418,16 @@ in
}; };
services.zfs.trim = { services.zfs.trim = {
enable = mkOption { enable = lib.mkOption {
description = "Whether to enable periodic TRIM on all ZFS pools."; description = "Whether to enable periodic TRIM on all ZFS pools.";
default = true; default = true;
example = false; example = false;
type = types.bool; type = lib.types.bool;
}; };
interval = mkOption { interval = lib.mkOption {
default = "weekly"; default = "weekly";
type = types.str; type = lib.types.str;
example = "daily"; example = "daily";
description = '' description = ''
How often we run trim. For most desktop and server systems How often we run trim. For most desktop and server systems
@ -441,9 +438,9 @@ in
''; '';
}; };
randomizedDelaySec = mkOption { randomizedDelaySec = lib.mkOption {
default = "6h"; default = "6h";
type = types.str; type = lib.types.str;
example = "12h"; example = "12h";
description = '' description = ''
Add a randomized delay before each ZFS trim. Add a randomized delay before each ZFS trim.
@ -455,11 +452,11 @@ in
}; };
services.zfs.autoScrub = { services.zfs.autoScrub = {
enable = mkEnableOption "periodic scrubbing of ZFS pools"; enable = lib.mkEnableOption "periodic scrubbing of ZFS pools";
interval = mkOption { interval = lib.mkOption {
default = "monthly"; default = "monthly";
type = types.str; type = lib.types.str;
example = "quarterly"; example = "quarterly";
description = '' description = ''
Systemd calendar expression when to scrub ZFS pools. See Systemd calendar expression when to scrub ZFS pools. See
@ -467,9 +464,9 @@ in
''; '';
}; };
randomizedDelaySec = mkOption { randomizedDelaySec = lib.mkOption {
default = "6h"; default = "6h";
type = types.str; type = lib.types.str;
example = "12h"; example = "12h";
description = '' description = ''
Add a randomized delay before each ZFS autoscrub. Add a randomized delay before each ZFS autoscrub.
@ -479,9 +476,9 @@ in
''; '';
}; };
pools = mkOption { pools = lib.mkOption {
default = []; default = [];
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
example = [ "tank" ]; example = [ "tank" ];
description = '' description = ''
List of ZFS pools to periodically scrub. If empty, all pools List of ZFS pools to periodically scrub. If empty, all pools
@ -490,8 +487,8 @@ in
}; };
}; };
services.zfs.expandOnBoot = mkOption { services.zfs.expandOnBoot = lib.mkOption {
type = types.either (types.enum [ "disabled" "all" ]) (types.listOf types.str); type = lib.types.either (lib.types.enum [ "disabled" "all" ]) (lib.types.listOf lib.types.str);
default = "disabled"; default = "disabled";
example = [ "tank" "dozer" ]; example = [ "tank" "dozer" ];
description = '' description = ''
@ -508,10 +505,10 @@ in
}; };
services.zfs.zed = { services.zfs.zed = {
enableMail = mkOption { enableMail = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = config.services.mail.sendmailSetuidWrapper != null; default = config.services.mail.sendmailSetuidWrapper != null;
defaultText = literalExpression '' defaultText = lib.literalExpression ''
config.services.mail.sendmailSetuidWrapper != null config.services.mail.sendmailSetuidWrapper != null
''; '';
description = '' description = ''
@ -519,9 +516,9 @@ in
''; '';
}; };
settings = mkOption { settings = lib.mkOption {
type = with types; attrsOf (oneOf [ str int bool (listOf str) ]); type = with lib.types; attrsOf (oneOf [ str int bool (listOf str) ]);
example = literalExpression '' example = lib.literalExpression ''
{ {
ZED_DEBUG_LOG = "/tmp/zed.debug.log"; ZED_DEBUG_LOG = "/tmp/zed.debug.log";
@ -549,8 +546,8 @@ in
###### implementation ###### implementation
config = mkMerge [ config = lib.mkMerge [
(mkIf cfgZfs.enabled { (lib.mkIf cfgZfs.enabled {
assertions = [ assertions = [
{ {
assertion = cfgZfs.modulePackage.version == cfgZfs.package.version; assertion = cfgZfs.modulePackage.version == cfgZfs.package.version;
@ -569,7 +566,7 @@ in
message = "boot.zfs.allowHibernation while force importing is enabled will cause data corruption"; message = "boot.zfs.allowHibernation while force importing is enabled will cause data corruption";
} }
{ {
assertion = !(elem "" allPools); assertion = !(lib.elem "" allPools);
message = '' message = ''
Automatic pool detection found an empty pool name, which can't be used. Automatic pool detection found an empty pool name, which can't be used.
Hint: for `fileSystems` entries with `fsType = zfs`, the `device` attribute Hint: for `fileSystems` entries with `fsType = zfs`, the `device` attribute
@ -591,10 +588,10 @@ in
]; ];
}; };
boot.initrd = mkIf inInitrd { boot.initrd = lib.mkIf inInitrd {
kernelModules = [ "zfs" ]; kernelModules = [ "zfs" ];
extraUtilsCommands = extraUtilsCommands =
mkIf (!config.boot.initrd.systemd.enable) '' lib.mkIf (!config.boot.initrd.systemd.enable) ''
copy_bin_and_libs ${cfgZfs.package}/sbin/zfs copy_bin_and_libs ${cfgZfs.package}/sbin/zfs
copy_bin_and_libs ${cfgZfs.package}/sbin/zdb copy_bin_and_libs ${cfgZfs.package}/sbin/zdb
copy_bin_and_libs ${cfgZfs.package}/sbin/zpool copy_bin_and_libs ${cfgZfs.package}/sbin/zpool
@ -602,12 +599,12 @@ in
copy_bin_and_libs ${cfgZfs.package}/lib/udev/zvol_id copy_bin_and_libs ${cfgZfs.package}/lib/udev/zvol_id
''; '';
extraUtilsCommandsTest = extraUtilsCommandsTest =
mkIf (!config.boot.initrd.systemd.enable) '' lib.mkIf (!config.boot.initrd.systemd.enable) ''
$out/bin/zfs --help >/dev/null 2>&1 $out/bin/zfs --help >/dev/null 2>&1
$out/bin/zpool --help >/dev/null 2>&1 $out/bin/zpool --help >/dev/null 2>&1
''; '';
postResumeCommands = mkIf (!config.boot.initrd.systemd.enable) (concatStringsSep "\n" (['' postResumeCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (lib.concatStringsSep "\n" ([''
ZFS_FORCE="${optionalString cfgZfs.forceImportRoot "-f"}" ZFS_FORCE="${lib.optionalString cfgZfs.forceImportRoot "-f"}"
''] ++ [(importLib { ''] ++ [(importLib {
# See comments at importLib definition. # See comments at importLib definition.
zpoolCmd = "zpool"; zpoolCmd = "zpool";
@ -629,21 +626,21 @@ in
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool. poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
fi fi
${optionalString config.boot.initrd.clevis.enable (concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem}") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets))} ${lib.optionalString config.boot.initrd.clevis.enable (lib.concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem}") (lib.filter (p: (lib.elemAt (lib.splitString "/" p) 0) == pool) clevisDatasets))}
${if isBool cfgZfs.requestEncryptionCredentials ${if lib.isBool cfgZfs.requestEncryptionCredentials
then optionalString cfgZfs.requestEncryptionCredentials '' then lib.optionalString cfgZfs.requestEncryptionCredentials ''
zfs load-key -a zfs load-key -a
'' ''
else concatMapStrings (fs: '' else lib.concatMapStrings (fs: ''
zfs load-key -- ${escapeShellArg fs} zfs load-key -- ${lib.escapeShellArg fs}
'') (filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)} '') (lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)}
'') rootPools))); '') rootPools)));
# Systemd in stage 1 # Systemd in stage 1
systemd = mkIf config.boot.initrd.systemd.enable { systemd = lib.mkIf config.boot.initrd.systemd.enable {
packages = [cfgZfs.package]; packages = [cfgZfs.package];
services = listToAttrs (map (pool: createImportService { services = lib.listToAttrs (map (pool: createImportService {
inherit pool; inherit pool;
systemd = config.boot.initrd.systemd.package; systemd = config.boot.initrd.systemd.package;
force = cfgZfs.forceImportRoot; force = cfgZfs.forceImportRoot;
@ -670,18 +667,18 @@ in
systemd.shutdownRamfs.storePaths = ["${cfgZfs.package}/bin/zpool"]; systemd.shutdownRamfs.storePaths = ["${cfgZfs.package}/bin/zpool"];
# TODO FIXME See https://github.com/NixOS/nixpkgs/pull/99386#issuecomment-798813567. To not break people's bootloader and as probably not everybody would read release notes that thoroughly add inSystem. # TODO FIXME See https://github.com/NixOS/nixpkgs/pull/99386#issuecomment-798813567. To not break people's bootloader and as probably not everybody would read release notes that thoroughly add inSystem.
boot.loader.grub = mkIf (inInitrd || inSystem) { boot.loader.grub = lib.mkIf (inInitrd || inSystem) {
zfsSupport = true; zfsSupport = true;
zfsPackage = cfgZfs.package; zfsPackage = cfgZfs.package;
}; };
services.zfs.zed.settings = { services.zfs.zed.settings = {
ZED_EMAIL_PROG = mkIf cfgZED.enableMail (mkDefault ( ZED_EMAIL_PROG = lib.mkIf cfgZED.enableMail (lib.mkDefault (
config.security.wrapperDir + "/" + config.security.wrapperDir + "/" +
config.services.mail.sendmailSetuidWrapper.program config.services.mail.sendmailSetuidWrapper.program
)); ));
# subject in header for sendmail # subject in header for sendmail
ZED_EMAIL_OPTS = mkIf cfgZED.enableMail (mkDefault "@ADDRESS@"); ZED_EMAIL_OPTS = lib.mkIf cfgZED.enableMail (lib.mkDefault "@ADDRESS@");
PATH = lib.makeBinPath [ PATH = lib.makeBinPath [
cfgZfs.package cfgZfs.package
@ -700,7 +697,7 @@ in
ACTION=="add|change", KERNEL=="sd[a-z]*[0-9]*|mmcblk[0-9]*p[0-9]*|nvme[0-9]*n[0-9]*p[0-9]*", ENV{ID_FS_TYPE}=="zfs_member", ATTR{../queue/scheduler}="none" ACTION=="add|change", KERNEL=="sd[a-z]*[0-9]*|mmcblk[0-9]*p[0-9]*|nvme[0-9]*n[0-9]*p[0-9]*", ENV{ID_FS_TYPE}=="zfs_member", ATTR{../queue/scheduler}="none"
''; '';
environment.etc = genAttrs environment.etc = lib.genAttrs
(map (map
(file: "zfs/zed.d/${file}") (file: "zfs/zed.d/${file}")
[ [
@ -725,7 +722,7 @@ in
system.fsPackages = [ cfgZfs.package ]; # XXX: needed? zfs doesn't have (need) a fsck system.fsPackages = [ cfgZfs.package ]; # XXX: needed? zfs doesn't have (need) a fsck
environment.systemPackages = [ cfgZfs.package ] environment.systemPackages = [ cfgZfs.package ]
++ optional cfgSnapshots.enable autosnapPkg; # so the user can run the command to see flags ++ lib.optional cfgSnapshots.enable autosnapPkg; # so the user can run the command to see flags
services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc. services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc.
systemd.packages = [ cfgZfs.package ]; systemd.packages = [ cfgZfs.package ];
@ -740,7 +737,7 @@ in
# This forces a sync of any ZFS pools prior to poweroff, even if they're set # This forces a sync of any ZFS pools prior to poweroff, even if they're set
# to sync=disabled. # to sync=disabled.
createSyncService = pool: createSyncService = pool:
nameValuePair "zfs-sync-${pool}" { lib.nameValuePair "zfs-sync-${pool}" {
description = "Sync ZFS pool \"${pool}\""; description = "Sync ZFS pool \"${pool}\"";
wantedBy = [ "shutdown.target" ]; wantedBy = [ "shutdown.target" ];
unitConfig = { unitConfig = {
@ -756,12 +753,12 @@ in
}; };
createZfsService = serv: createZfsService = serv:
nameValuePair serv { lib.nameValuePair serv {
after = [ "systemd-modules-load.service" ]; after = [ "systemd-modules-load.service" ];
wantedBy = [ "zfs.target" ]; wantedBy = [ "zfs.target" ];
}; };
in listToAttrs (map createImportService' dataPools ++ in lib.listToAttrs (map createImportService' dataPools ++
map createSyncService allPools ++ map createSyncService allPools ++
map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]); map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
@ -770,7 +767,7 @@ in
systemd.targets.zfs.wantedBy = [ "multi-user.target" ]; systemd.targets.zfs.wantedBy = [ "multi-user.target" ];
}) })
(mkIf (cfgZfs.enabled && cfgExpandOnBoot != "disabled") { (lib.mkIf (cfgZfs.enabled && cfgExpandOnBoot != "disabled") {
systemd.services."zpool-expand@" = { systemd.services."zpool-expand@" = {
description = "Expand ZFS pools"; description = "Expand ZFS pools";
after = [ "zfs.target" ]; after = [ "zfs.target" ];
@ -823,7 +820,7 @@ in
}; };
}) })
(mkIf (cfgZfs.enabled && cfgSnapshots.enable) { (lib.mkIf (cfgZfs.enabled && cfgSnapshots.enable) {
systemd.services = let systemd.services = let
descr = name: if name == "frequent" then "15 mins" descr = name: if name == "frequent" then "15 mins"
else if name == "hourly" then "hour" else if name == "hourly" then "hour"
@ -861,7 +858,7 @@ in
}) snapshotNames); }) snapshotNames);
}) })
(mkIf (cfgZfs.enabled && cfgScrub.enable) { (lib.mkIf (cfgZfs.enabled && cfgScrub.enable) {
systemd.services.zfs-scrub = { systemd.services.zfs-scrub = {
description = "ZFS pools scrubbing"; description = "ZFS pools scrubbing";
after = [ "zfs-import.target" ]; after = [ "zfs-import.target" ];
@ -871,7 +868,7 @@ in
script = '' script = ''
${cfgZfs.package}/bin/zpool scrub -w ${ ${cfgZfs.package}/bin/zpool scrub -w ${
if cfgScrub.pools != [] then if cfgScrub.pools != [] then
(concatStringsSep " " cfgScrub.pools) (lib.concatStringsSep " " cfgScrub.pools)
else else
"$(${cfgZfs.package}/bin/zpool list -H -o name)" "$(${cfgZfs.package}/bin/zpool list -H -o name)"
} }
@ -889,7 +886,7 @@ in
}; };
}) })
(mkIf (cfgZfs.enabled && cfgTrim.enable) { (lib.mkIf (cfgZfs.enabled && cfgTrim.enable) {
systemd.services.zpool-trim = { systemd.services.zpool-trim = {
description = "ZFS pools trim"; description = "ZFS pools trim";
after = [ "zfs-import.target" ]; after = [ "zfs-import.target" ];