mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-12 04:35:41 +03:00
treewide: Format all Nix files
Format all Nix files using the officially approved formatter,
making the CI check introduced in the previous commit succeed:
nix-build ci -A fmt.check
This is the next step of the of the [implementation](https://github.com/NixOS/nixfmt/issues/153)
of the accepted [RFC 166](https://github.com/NixOS/rfcs/pull/166).
This commit will lead to merge conflicts for a number of PRs,
up to an estimated ~1100 (~33%) among the PRs with activity in the past 2
months, but that should be lower than what it would be without the previous
[partial treewide format](https://github.com/NixOS/nixpkgs/pull/322537).
Merge conflicts caused by this commit can now automatically be resolved while rebasing using the
[auto-rebase script](8616af08d9/maintainers/scripts/auto-rebase
).
If you run into any problems regarding any of this, please reach out to the
[formatting team](https://nixos.org/community/teams/formatting/) by
pinging @NixOS/nix-formatting.
This commit is contained in:
parent
2140bf39e4
commit
374e6bcc40
1523 changed files with 986047 additions and 513621 deletions
|
@ -13,16 +13,17 @@ let
|
|||
# the mount units for the key file are done; i.e. no special
|
||||
# treatment is needed.
|
||||
lateEncDevs =
|
||||
if config.boot.initrd.systemd.enable
|
||||
then { }
|
||||
else filter (dev: dev.encrypted.keyFile != null) encDevs;
|
||||
if config.boot.initrd.systemd.enable then
|
||||
{ }
|
||||
else
|
||||
filter (dev: dev.encrypted.keyFile != null) encDevs;
|
||||
earlyEncDevs =
|
||||
if config.boot.initrd.systemd.enable
|
||||
then encDevs
|
||||
else filter (dev: dev.encrypted.keyFile == null) encDevs;
|
||||
if config.boot.initrd.systemd.enable then
|
||||
encDevs
|
||||
else
|
||||
filter (dev: dev.encrypted.keyFile == null) encDevs;
|
||||
|
||||
anyEncrypted =
|
||||
foldr (j: v: v || j.encrypted.enable) false encDevs;
|
||||
anyEncrypted = foldr (j: v: v || j.encrypted.enable) false encDevs;
|
||||
|
||||
encryptedFSOptions = {
|
||||
|
||||
|
@ -88,9 +89,13 @@ in
|
|||
}
|
||||
{
|
||||
assertion =
|
||||
config.boot.initrd.systemd.enable -> (
|
||||
config.boot.initrd.systemd.enable
|
||||
-> (
|
||||
dev.encrypted.keyFile == null
|
||||
|| !lib.any (x: lib.hasPrefix x dev.encrypted.keyFile) ["/mnt-root" "$targetRoot"]
|
||||
|| !lib.any (x: lib.hasPrefix x dev.encrypted.keyFile) [
|
||||
"/mnt-root"
|
||||
"$targetRoot"
|
||||
]
|
||||
);
|
||||
message = ''
|
||||
Bad use of '/mnt-root' or '$targetRoot` in 'keyFile'.
|
||||
|
@ -103,18 +108,24 @@ in
|
|||
|
||||
boot.initrd = {
|
||||
luks = {
|
||||
devices =
|
||||
builtins.listToAttrs (map (dev: {
|
||||
devices = builtins.listToAttrs (
|
||||
map (dev: {
|
||||
name = dev.encrypted.label;
|
||||
value = { device = dev.encrypted.blkDev; inherit (dev.encrypted) keyFile; };
|
||||
}) earlyEncDevs);
|
||||
value = {
|
||||
device = dev.encrypted.blkDev;
|
||||
inherit (dev.encrypted) keyFile;
|
||||
};
|
||||
}) earlyEncDevs
|
||||
);
|
||||
forceLuksSupportInInitrd = true;
|
||||
};
|
||||
# TODO: systemd stage 1
|
||||
postMountCommands = lib.mkIf (!config.boot.initrd.systemd.enable)
|
||||
(concatMapStrings (dev:
|
||||
postMountCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
|
||||
concatMapStrings (
|
||||
dev:
|
||||
"cryptsetup luksOpen --key-file ${dev.encrypted.keyFile} ${dev.encrypted.blkDev} ${dev.encrypted.label};\n"
|
||||
) lateEncDevs);
|
||||
) lateEncDevs
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
{ config, lib, pkgs, utils, ... }@moduleArgs:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}@moduleArgs:
|
||||
|
||||
with lib;
|
||||
with utils;
|
||||
|
@ -7,217 +13,259 @@ let
|
|||
# https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
|
||||
escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string;
|
||||
|
||||
addCheckDesc = desc: elemType: check: types.addCheck elemType check
|
||||
// { description = "${elemType.description} (with check: ${desc})"; };
|
||||
addCheckDesc =
|
||||
desc: elemType: check:
|
||||
types.addCheck elemType check // { description = "${elemType.description} (with check: ${desc})"; };
|
||||
|
||||
isNonEmpty = s: (builtins.match "[ \t\n]*" s) == null;
|
||||
nonEmptyStr = addCheckDesc "non-empty" types.str isNonEmpty;
|
||||
|
||||
fileSystems' = toposort fsBefore (attrValues config.fileSystems);
|
||||
|
||||
fileSystems = if fileSystems' ? result
|
||||
then # use topologically sorted fileSystems everywhere
|
||||
fileSystems'.result
|
||||
else # the assertion below will catch this,
|
||||
# but we fall back to the original order
|
||||
# anyway so that other modules could check
|
||||
# their assertions too
|
||||
(attrValues config.fileSystems);
|
||||
fileSystems =
|
||||
if fileSystems' ? result then
|
||||
# use topologically sorted fileSystems everywhere
|
||||
fileSystems'.result
|
||||
else
|
||||
# the assertion below will catch this,
|
||||
# but we fall back to the original order
|
||||
# anyway so that other modules could check
|
||||
# their assertions too
|
||||
(attrValues config.fileSystems);
|
||||
|
||||
specialFSTypes = [ "proc" "sysfs" "tmpfs" "ramfs" "devtmpfs" "devpts" ];
|
||||
specialFSTypes = [
|
||||
"proc"
|
||||
"sysfs"
|
||||
"tmpfs"
|
||||
"ramfs"
|
||||
"devtmpfs"
|
||||
"devpts"
|
||||
];
|
||||
|
||||
nonEmptyWithoutTrailingSlash = addCheckDesc "non-empty without trailing slash" types.str
|
||||
(s: isNonEmpty s && (builtins.match ".+/" s) == null);
|
||||
nonEmptyWithoutTrailingSlash = addCheckDesc "non-empty without trailing slash" types.str (
|
||||
s: isNonEmpty s && (builtins.match ".+/" s) == null
|
||||
);
|
||||
|
||||
coreFileSystemOpts = { name, config, ... }: {
|
||||
coreFileSystemOpts =
|
||||
{ name, config, ... }:
|
||||
{
|
||||
|
||||
options = {
|
||||
enable = mkEnableOption "the filesystem mount" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
mountPoint = mkOption {
|
||||
example = "/mnt/usb";
|
||||
type = nonEmptyWithoutTrailingSlash;
|
||||
description = "Location of the mounted file system.";
|
||||
};
|
||||
|
||||
stratis.poolUuid = lib.mkOption {
|
||||
type = types.uniq (types.nullOr types.str);
|
||||
description = ''
|
||||
UUID of the stratis pool that the fs is located in
|
||||
'';
|
||||
example = "04c68063-90a5-4235-b9dd-6180098a20d9";
|
||||
default = null;
|
||||
};
|
||||
|
||||
device = mkOption {
|
||||
default = null;
|
||||
example = "/dev/sda";
|
||||
type = types.nullOr nonEmptyStr;
|
||||
description = "Location of the device.";
|
||||
};
|
||||
|
||||
fsType = mkOption {
|
||||
default = "auto";
|
||||
example = "ext3";
|
||||
type = nonEmptyStr;
|
||||
description = "Type of the file system.";
|
||||
};
|
||||
|
||||
options = mkOption {
|
||||
default = [ "defaults" ];
|
||||
example = [ "data=journal" ];
|
||||
description = ''
|
||||
Options used to mount the file system.
|
||||
See {manpage}`mount(8)` for common options.
|
||||
'';
|
||||
type = types.nonEmptyListOf nonEmptyStr;
|
||||
};
|
||||
|
||||
depends = mkOption {
|
||||
default = [ ];
|
||||
example = [ "/persist" ];
|
||||
type = types.listOf nonEmptyWithoutTrailingSlash;
|
||||
description = ''
|
||||
List of paths that should be mounted before this one. This filesystem's
|
||||
{option}`device` and {option}`mountPoint` are always
|
||||
checked and do not need to be included explicitly. If a path is added
|
||||
to this list, any other filesystem whose mount point is a parent of
|
||||
the path will be mounted before this filesystem. The paths do not need
|
||||
to actually be the {option}`mountPoint` of some other filesystem.
|
||||
'';
|
||||
};
|
||||
|
||||
options = {
|
||||
enable = mkEnableOption "the filesystem mount" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
mountPoint = mkOption {
|
||||
example = "/mnt/usb";
|
||||
type = nonEmptyWithoutTrailingSlash;
|
||||
description = "Location of the mounted file system.";
|
||||
};
|
||||
|
||||
stratis.poolUuid = lib.mkOption {
|
||||
type = types.uniq (types.nullOr types.str);
|
||||
description = ''
|
||||
UUID of the stratis pool that the fs is located in
|
||||
'';
|
||||
example = "04c68063-90a5-4235-b9dd-6180098a20d9";
|
||||
default = null;
|
||||
};
|
||||
|
||||
device = mkOption {
|
||||
default = null;
|
||||
example = "/dev/sda";
|
||||
type = types.nullOr nonEmptyStr;
|
||||
description = "Location of the device.";
|
||||
};
|
||||
|
||||
fsType = mkOption {
|
||||
default = "auto";
|
||||
example = "ext3";
|
||||
type = nonEmptyStr;
|
||||
description = "Type of the file system.";
|
||||
};
|
||||
|
||||
options = mkOption {
|
||||
default = [ "defaults" ];
|
||||
example = [ "data=journal" ];
|
||||
description = ''
|
||||
Options used to mount the file system.
|
||||
See {manpage}`mount(8)` for common options.
|
||||
'';
|
||||
type = types.nonEmptyListOf nonEmptyStr;
|
||||
};
|
||||
|
||||
depends = mkOption {
|
||||
default = [ ];
|
||||
example = [ "/persist" ];
|
||||
type = types.listOf nonEmptyWithoutTrailingSlash;
|
||||
description = ''
|
||||
List of paths that should be mounted before this one. This filesystem's
|
||||
{option}`device` and {option}`mountPoint` are always
|
||||
checked and do not need to be included explicitly. If a path is added
|
||||
to this list, any other filesystem whose mount point is a parent of
|
||||
the path will be mounted before this filesystem. The paths do not need
|
||||
to actually be the {option}`mountPoint` of some other filesystem.
|
||||
'';
|
||||
config = {
|
||||
mountPoint = mkDefault name;
|
||||
device = mkIf (elem config.fsType specialFSTypes) (mkDefault config.fsType);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
mountPoint = mkDefault name;
|
||||
device = mkIf (elem config.fsType specialFSTypes) (mkDefault config.fsType);
|
||||
};
|
||||
fileSystemOpts =
|
||||
{ config, ... }:
|
||||
{
|
||||
|
||||
};
|
||||
options = {
|
||||
|
||||
fileSystemOpts = { config, ... }: {
|
||||
label = mkOption {
|
||||
default = null;
|
||||
example = "root-partition";
|
||||
type = types.nullOr nonEmptyStr;
|
||||
description = "Label of the device (if any).";
|
||||
};
|
||||
|
||||
options = {
|
||||
autoFormat = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If the device does not currently contain a filesystem (as
|
||||
determined by {command}`blkid`), then automatically
|
||||
format it with the filesystem type specified in
|
||||
{option}`fsType`. Use with caution.
|
||||
'';
|
||||
};
|
||||
|
||||
formatOptions = mkOption {
|
||||
visible = false;
|
||||
type = types.unspecified;
|
||||
default = null;
|
||||
};
|
||||
|
||||
autoResize = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If set, the filesystem is grown to its maximum size before
|
||||
being mounted. (This is typically the size of the containing
|
||||
partition.) This is currently only supported for ext2/3/4
|
||||
filesystems that are mounted during early boot.
|
||||
'';
|
||||
};
|
||||
|
||||
noCheck = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = "Disable running fsck on this filesystem.";
|
||||
};
|
||||
|
||||
label = mkOption {
|
||||
default = null;
|
||||
example = "root-partition";
|
||||
type = types.nullOr nonEmptyStr;
|
||||
description = "Label of the device (if any).";
|
||||
};
|
||||
|
||||
autoFormat = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If the device does not currently contain a filesystem (as
|
||||
determined by {command}`blkid`), then automatically
|
||||
format it with the filesystem type specified in
|
||||
{option}`fsType`. Use with caution.
|
||||
'';
|
||||
};
|
||||
config.device = lib.mkIf (config.label != null) (
|
||||
lib.mkDefault "/dev/disk/by-label/${escape config.label}"
|
||||
);
|
||||
|
||||
formatOptions = mkOption {
|
||||
visible = false;
|
||||
type = types.unspecified;
|
||||
default = null;
|
||||
};
|
||||
|
||||
autoResize = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If set, the filesystem is grown to its maximum size before
|
||||
being mounted. (This is typically the size of the containing
|
||||
partition.) This is currently only supported for ext2/3/4
|
||||
filesystems that are mounted during early boot.
|
||||
'';
|
||||
};
|
||||
|
||||
noCheck = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = "Disable running fsck on this filesystem.";
|
||||
};
|
||||
config.options =
|
||||
let
|
||||
inInitrd = utils.fsNeededForBoot config;
|
||||
in
|
||||
mkMerge [
|
||||
(mkIf config.autoResize [ "x-systemd.growfs" ])
|
||||
(mkIf config.autoFormat [ "x-systemd.makefs" ])
|
||||
(mkIf (utils.fsNeededForBoot config) [ "x-initrd.mount" ])
|
||||
(mkIf
|
||||
# With scripted stage 1, depends is implemented by sorting 'config.system.build.fileSystems'
|
||||
(lib.length config.depends > 0 && (inInitrd -> moduleArgs.config.boot.initrd.systemd.enable))
|
||||
(map (x: "x-systemd.requires-mounts-for=${optionalString inInitrd "/sysroot"}${x}") config.depends)
|
||||
)
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
config.device = lib.mkIf (config.label != null) (lib.mkDefault "/dev/disk/by-label/${escape config.label}");
|
||||
|
||||
config.options = let
|
||||
inInitrd = utils.fsNeededForBoot config;
|
||||
in mkMerge [
|
||||
(mkIf config.autoResize [ "x-systemd.growfs" ])
|
||||
(mkIf config.autoFormat [ "x-systemd.makefs" ])
|
||||
(mkIf (utils.fsNeededForBoot config) [ "x-initrd.mount" ])
|
||||
(mkIf
|
||||
# With scripted stage 1, depends is implemented by sorting 'config.system.build.fileSystems'
|
||||
(lib.length config.depends > 0 && (inInitrd -> moduleArgs.config.boot.initrd.systemd.enable))
|
||||
(
|
||||
map (
|
||||
x: "x-systemd.requires-mounts-for=${optionalString inInitrd "/sysroot"}${x}"
|
||||
) config.depends
|
||||
)
|
||||
)
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
# Makes sequence of `specialMount device mountPoint options fsType` commands.
|
||||
# `systemMount` should be defined in the sourcing script.
|
||||
makeSpecialMounts = mounts:
|
||||
pkgs.writeText "mounts.sh" (concatMapStringsSep "\n" (mount: ''
|
||||
specialMount "${mount.device}" "${mount.mountPoint}" "${concatStringsSep "," mount.options}" "${mount.fsType}"
|
||||
'') mounts);
|
||||
makeSpecialMounts =
|
||||
mounts:
|
||||
pkgs.writeText "mounts.sh" (
|
||||
concatMapStringsSep "\n" (mount: ''
|
||||
specialMount "${mount.device}" "${mount.mountPoint}" "${concatStringsSep "," mount.options}" "${mount.fsType}"
|
||||
'') mounts
|
||||
);
|
||||
|
||||
makeFstabEntries =
|
||||
let
|
||||
fsToSkipCheck = [
|
||||
"none"
|
||||
"auto"
|
||||
"overlay"
|
||||
"iso9660"
|
||||
"bindfs"
|
||||
"udf"
|
||||
"btrfs"
|
||||
"zfs"
|
||||
"tmpfs"
|
||||
"bcachefs"
|
||||
"nfs"
|
||||
"nfs4"
|
||||
"nilfs2"
|
||||
"vboxsf"
|
||||
"squashfs"
|
||||
"glusterfs"
|
||||
"apfs"
|
||||
"9p"
|
||||
"cifs"
|
||||
"prl_fs"
|
||||
"vmhgfs"
|
||||
] ++ lib.optionals (!config.boot.initrd.checkJournalingFS) [
|
||||
"ext3"
|
||||
"ext4"
|
||||
"reiserfs"
|
||||
"xfs"
|
||||
"jfs"
|
||||
"f2fs"
|
||||
];
|
||||
fsToSkipCheck =
|
||||
[
|
||||
"none"
|
||||
"auto"
|
||||
"overlay"
|
||||
"iso9660"
|
||||
"bindfs"
|
||||
"udf"
|
||||
"btrfs"
|
||||
"zfs"
|
||||
"tmpfs"
|
||||
"bcachefs"
|
||||
"nfs"
|
||||
"nfs4"
|
||||
"nilfs2"
|
||||
"vboxsf"
|
||||
"squashfs"
|
||||
"glusterfs"
|
||||
"apfs"
|
||||
"9p"
|
||||
"cifs"
|
||||
"prl_fs"
|
||||
"vmhgfs"
|
||||
]
|
||||
++ lib.optionals (!config.boot.initrd.checkJournalingFS) [
|
||||
"ext3"
|
||||
"ext4"
|
||||
"reiserfs"
|
||||
"xfs"
|
||||
"jfs"
|
||||
"f2fs"
|
||||
];
|
||||
isBindMount = fs: builtins.elem "bind" fs.options;
|
||||
skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck || isBindMount fs;
|
||||
in fstabFileSystems: { }: concatMapStrings (fs:
|
||||
(if fs.device != null then escape fs.device
|
||||
else throw "No device specified for mount point ‘${fs.mountPoint}’.")
|
||||
+ " " + escape fs.mountPoint
|
||||
+ " " + fs.fsType
|
||||
+ " " + escape (builtins.concatStringsSep "," fs.options)
|
||||
+ " 0 " + (if skipCheck fs then "0" else if fs.mountPoint == "/" then "1" else "2")
|
||||
skipCheck =
|
||||
fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck || isBindMount fs;
|
||||
in
|
||||
fstabFileSystems:
|
||||
{ }:
|
||||
concatMapStrings (
|
||||
fs:
|
||||
(
|
||||
if fs.device != null then
|
||||
escape fs.device
|
||||
else
|
||||
throw "No device specified for mount point ‘${fs.mountPoint}’."
|
||||
)
|
||||
+ " "
|
||||
+ escape fs.mountPoint
|
||||
+ " "
|
||||
+ fs.fsType
|
||||
+ " "
|
||||
+ escape (builtins.concatStringsSep "," fs.options)
|
||||
+ " 0 "
|
||||
+ (
|
||||
if skipCheck fs then
|
||||
"0"
|
||||
else if fs.mountPoint == "/" then
|
||||
"1"
|
||||
else
|
||||
"2"
|
||||
)
|
||||
+ "\n"
|
||||
) fstabFileSystems;
|
||||
|
||||
initrdFstab = pkgs.writeText "initrd-fstab" (makeFstabEntries (filter utils.fsNeededForBoot fileSystems) { });
|
||||
initrdFstab = pkgs.writeText "initrd-fstab" (
|
||||
makeFstabEntries (filter utils.fsNeededForBoot fileSystems) { }
|
||||
);
|
||||
|
||||
in
|
||||
|
||||
|
@ -228,7 +276,7 @@ in
|
|||
options = {
|
||||
|
||||
fileSystems = mkOption {
|
||||
default = {};
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
"/".device = "/dev/hda1";
|
||||
|
@ -240,7 +288,12 @@ in
|
|||
"/bigdisk".label = "bigdisk";
|
||||
}
|
||||
'';
|
||||
type = types.attrsOf (types.submodule [coreFileSystemOpts fileSystemOpts]);
|
||||
type = types.attrsOf (
|
||||
types.submodule [
|
||||
coreFileSystemOpts
|
||||
fileSystemOpts
|
||||
]
|
||||
);
|
||||
apply = lib.filterAttrs (_: fs: fs.enable);
|
||||
description = ''
|
||||
The file systems to be mounted. It must include an entry for
|
||||
|
@ -273,10 +326,9 @@ in
|
|||
zfs = lib.mkForce false;
|
||||
}
|
||||
'';
|
||||
type = types.coercedTo
|
||||
(types.listOf types.str)
|
||||
(enabled: lib.listToAttrs (map (fs: lib.nameValuePair fs true) enabled))
|
||||
(types.attrsOf types.bool);
|
||||
type = types.coercedTo (types.listOf types.str) (
|
||||
enabled: lib.listToAttrs (map (fs: lib.nameValuePair fs true) enabled)
|
||||
) (types.attrsOf types.bool);
|
||||
description = ''
|
||||
Names of supported filesystem types, or an attribute set of file system types
|
||||
and their state. The set form may be used together with `lib.mkForce` to
|
||||
|
@ -286,7 +338,7 @@ in
|
|||
};
|
||||
|
||||
boot.specialFileSystems = mkOption {
|
||||
default = {};
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule coreFileSystemOpts);
|
||||
apply = lib.filterAttrs (_: fs: fs.enable);
|
||||
internal = true;
|
||||
|
@ -326,70 +378,89 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = {
|
||||
|
||||
assertions = let
|
||||
ls = sep: concatMapStringsSep sep (x: x.mountPoint);
|
||||
resizableFSes = [
|
||||
"ext3"
|
||||
"ext4"
|
||||
"btrfs"
|
||||
"xfs"
|
||||
];
|
||||
notAutoResizable = fs: fs.autoResize && !(builtins.elem fs.fsType resizableFSes);
|
||||
in [
|
||||
{ assertion = ! (fileSystems' ? cycle);
|
||||
message = "The ‘fileSystems’ option can't be topologically sorted: mountpoint dependency path ${ls " -> " fileSystems'.cycle} loops to ${ls ", " fileSystems'.loops}";
|
||||
}
|
||||
{ assertion = ! (any notAutoResizable fileSystems);
|
||||
message = let
|
||||
fs = head (filter notAutoResizable fileSystems);
|
||||
in ''
|
||||
Mountpoint '${fs.mountPoint}': 'autoResize = true' is not supported for 'fsType = "${fs.fsType}"'
|
||||
${optionalString (fs.fsType == "auto") "fsType has to be explicitly set and"}
|
||||
only the following support it: ${lib.concatStringsSep ", " resizableFSes}.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = ! (any (fs: fs.formatOptions != null) fileSystems);
|
||||
assertions =
|
||||
let
|
||||
ls = sep: concatMapStringsSep sep (x: x.mountPoint);
|
||||
resizableFSes = [
|
||||
"ext3"
|
||||
"ext4"
|
||||
"btrfs"
|
||||
"xfs"
|
||||
];
|
||||
notAutoResizable = fs: fs.autoResize && !(builtins.elem fs.fsType resizableFSes);
|
||||
in
|
||||
[
|
||||
{
|
||||
assertion = !(fileSystems' ? cycle);
|
||||
message = "The ‘fileSystems’ option can't be topologically sorted: mountpoint dependency path ${ls " -> " fileSystems'.cycle} loops to ${ls ", " fileSystems'.loops}";
|
||||
}
|
||||
{
|
||||
assertion = !(any notAutoResizable fileSystems);
|
||||
message =
|
||||
let
|
||||
fs = head (filter notAutoResizable fileSystems);
|
||||
in
|
||||
''
|
||||
Mountpoint '${fs.mountPoint}': 'autoResize = true' is not supported for 'fsType = "${fs.fsType}"'
|
||||
${optionalString (fs.fsType == "auto") "fsType has to be explicitly set and"}
|
||||
only the following support it: ${lib.concatStringsSep ", " resizableFSes}.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = !(any (fs: fs.formatOptions != null) fileSystems);
|
||||
message = ''
|
||||
'fileSystems.<name>.formatOptions' has been removed, since
|
||||
systemd-makefs does not support any way to provide formatting
|
||||
options.
|
||||
'';
|
||||
}
|
||||
]
|
||||
++ lib.map (fs: {
|
||||
assertion = fs.label != null -> fs.device == "/dev/disk/by-label/${escape fs.label}";
|
||||
message = ''
|
||||
'fileSystems.<name>.formatOptions' has been removed, since
|
||||
systemd-makefs does not support any way to provide formatting
|
||||
options.
|
||||
The filesystem with mount point ${fs.mountPoint} has its label and device set to inconsistent values:
|
||||
label: ${toString fs.label}
|
||||
device: ${toString fs.device}
|
||||
'filesystems.<name>.label' and 'filesystems.<name>.device' are mutually exclusive. Please set only one.
|
||||
'';
|
||||
}
|
||||
] ++ lib.map (fs: {
|
||||
assertion = fs.label != null -> fs.device == "/dev/disk/by-label/${escape fs.label}";
|
||||
message = ''
|
||||
The filesystem with mount point ${fs.mountPoint} has its label and device set to inconsistent values:
|
||||
label: ${toString fs.label}
|
||||
device: ${toString fs.device}
|
||||
'filesystems.<name>.label' and 'filesystems.<name>.device' are mutually exclusive. Please set only one.
|
||||
'';
|
||||
}) fileSystems;
|
||||
}) fileSystems;
|
||||
|
||||
# Export for use in other modules
|
||||
system.build.fileSystems = fileSystems;
|
||||
system.build.earlyMountScript = makeSpecialMounts (toposort fsBefore (attrValues config.boot.specialFileSystems)).result;
|
||||
system.build.earlyMountScript = makeSpecialMounts (toposort fsBefore (
|
||||
attrValues config.boot.specialFileSystems
|
||||
)).result;
|
||||
|
||||
boot.supportedFilesystems = map (fs: fs.fsType) fileSystems;
|
||||
|
||||
# Add the mount helpers to the system path so that `mount' can find them.
|
||||
system.fsPackages = [ pkgs.dosfstools ];
|
||||
|
||||
environment.systemPackages = with pkgs; [ fuse3 fuse ] ++ config.system.fsPackages;
|
||||
environment.systemPackages =
|
||||
with pkgs;
|
||||
[
|
||||
fuse3
|
||||
fuse
|
||||
]
|
||||
++ config.system.fsPackages;
|
||||
|
||||
environment.etc.fstab.text =
|
||||
let
|
||||
swapOptions = sw: concatStringsSep "," (
|
||||
sw.options
|
||||
++ optional (sw.priority != null) "pri=${toString sw.priority}"
|
||||
++ optional (sw.discardPolicy != null) "discard${optionalString (sw.discardPolicy != "both") "=${toString sw.discardPolicy}"}"
|
||||
);
|
||||
in ''
|
||||
swapOptions =
|
||||
sw:
|
||||
concatStringsSep "," (
|
||||
sw.options
|
||||
++ optional (sw.priority != null) "pri=${toString sw.priority}"
|
||||
++
|
||||
optional (sw.discardPolicy != null)
|
||||
"discard${optionalString (sw.discardPolicy != "both") "=${toString sw.discardPolicy}"}"
|
||||
);
|
||||
in
|
||||
''
|
||||
# This is a generated file. Do not edit!
|
||||
#
|
||||
# To make changes, edit the fileSystems and swapDevices NixOS options
|
||||
|
@ -398,59 +469,63 @@ in
|
|||
# <file system> <mount point> <type> <options> <dump> <pass>
|
||||
|
||||
# Filesystems.
|
||||
${makeFstabEntries fileSystems {}}
|
||||
${makeFstabEntries fileSystems { }}
|
||||
|
||||
${lib.optionalString (config.swapDevices != []) "# Swap devices."}
|
||||
${flip concatMapStrings config.swapDevices (sw:
|
||||
"${sw.realDevice} none swap ${swapOptions sw}\n"
|
||||
)}
|
||||
${lib.optionalString (config.swapDevices != [ ]) "# Swap devices."}
|
||||
${flip concatMapStrings config.swapDevices (sw: "${sw.realDevice} none swap ${swapOptions sw}\n")}
|
||||
'';
|
||||
|
||||
boot.initrd.systemd.storePaths = [initrdFstab];
|
||||
boot.initrd.systemd.storePaths = [ initrdFstab ];
|
||||
boot.initrd.systemd.managerEnvironment.SYSTEMD_SYSROOT_FSTAB = initrdFstab;
|
||||
boot.initrd.systemd.services.initrd-parse-etc.environment.SYSTEMD_SYSROOT_FSTAB = initrdFstab;
|
||||
|
||||
# Provide a target that pulls in all filesystems.
|
||||
systemd.targets.fs =
|
||||
{ description = "All File Systems";
|
||||
wants = [ "local-fs.target" "remote-fs.target" ];
|
||||
};
|
||||
systemd.targets.fs = {
|
||||
description = "All File Systems";
|
||||
wants = [
|
||||
"local-fs.target"
|
||||
"remote-fs.target"
|
||||
];
|
||||
};
|
||||
|
||||
systemd.services = {
|
||||
# Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore.
|
||||
# This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then.
|
||||
"mount-pstore" = {
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
# skip on kernels without the pstore module
|
||||
ExecCondition = "${pkgs.kmod}/bin/modprobe -b pstore";
|
||||
ExecStart = pkgs.writeShellScript "mount-pstore.sh" ''
|
||||
set -eu
|
||||
# if the pstore module is builtin it will have mounted the persistent store automatically. it may also be already mounted for other reasons.
|
||||
${pkgs.util-linux}/bin/mountpoint -q /sys/fs/pstore || ${pkgs.util-linux}/bin/mount -t pstore -o nosuid,noexec,nodev pstore /sys/fs/pstore
|
||||
# wait up to 1.5 seconds for the backend to be registered and the files to appear. a systemd path unit cannot detect this happening; and succeeding after a restart would not start dependent units.
|
||||
TRIES=15
|
||||
while [ "$(cat /sys/module/pstore/parameters/backend)" = "(null)" ]; do
|
||||
if (( $TRIES )); then
|
||||
sleep 0.1
|
||||
TRIES=$((TRIES-1))
|
||||
else
|
||||
echo "Persistent Storage backend was not registered in time." >&2
|
||||
break
|
||||
fi
|
||||
done
|
||||
'';
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
unitConfig = {
|
||||
ConditionVirtualization = "!container";
|
||||
DefaultDependencies = false; # needed to prevent a cycle
|
||||
};
|
||||
before = [ "systemd-pstore.service" "shutdown.target" ];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
wantedBy = [ "systemd-pstore.service" ];
|
||||
# Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore.
|
||||
# This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then.
|
||||
"mount-pstore" = {
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
# skip on kernels without the pstore module
|
||||
ExecCondition = "${pkgs.kmod}/bin/modprobe -b pstore";
|
||||
ExecStart = pkgs.writeShellScript "mount-pstore.sh" ''
|
||||
set -eu
|
||||
# if the pstore module is builtin it will have mounted the persistent store automatically. it may also be already mounted for other reasons.
|
||||
${pkgs.util-linux}/bin/mountpoint -q /sys/fs/pstore || ${pkgs.util-linux}/bin/mount -t pstore -o nosuid,noexec,nodev pstore /sys/fs/pstore
|
||||
# wait up to 1.5 seconds for the backend to be registered and the files to appear. a systemd path unit cannot detect this happening; and succeeding after a restart would not start dependent units.
|
||||
TRIES=15
|
||||
while [ "$(cat /sys/module/pstore/parameters/backend)" = "(null)" ]; do
|
||||
if (( $TRIES )); then
|
||||
sleep 0.1
|
||||
TRIES=$((TRIES-1))
|
||||
else
|
||||
echo "Persistent Storage backend was not registered in time." >&2
|
||||
break
|
||||
fi
|
||||
done
|
||||
'';
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
unitConfig = {
|
||||
ConditionVirtualization = "!container";
|
||||
DefaultDependencies = false; # needed to prevent a cycle
|
||||
};
|
||||
before = [
|
||||
"systemd-pstore.service"
|
||||
"shutdown.target"
|
||||
];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
wantedBy = [ "systemd-pstore.service" ];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /run/keys 0750 root ${toString config.ids.gids.keys}"
|
||||
|
@ -458,21 +533,79 @@ in
|
|||
];
|
||||
|
||||
# Sync mount options with systemd's src/core/mount-setup.c: mount_table.
|
||||
boot.specialFileSystems = {
|
||||
"/proc" = { fsType = "proc"; options = [ "nosuid" "noexec" "nodev" ]; };
|
||||
"/run" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
|
||||
"/dev" = { fsType = "devtmpfs"; options = [ "nosuid" "strictatime" "mode=755" "size=${config.boot.devSize}" ]; };
|
||||
"/dev/shm" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=1777" "size=${config.boot.devShmSize}" ]; };
|
||||
"/dev/pts" = { fsType = "devpts"; options = [ "nosuid" "noexec" "mode=620" "ptmxmode=0666" "gid=${toString config.ids.gids.tty}" ]; };
|
||||
boot.specialFileSystems =
|
||||
{
|
||||
"/proc" = {
|
||||
fsType = "proc";
|
||||
options = [
|
||||
"nosuid"
|
||||
"noexec"
|
||||
"nodev"
|
||||
];
|
||||
};
|
||||
"/run" = {
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"nosuid"
|
||||
"nodev"
|
||||
"strictatime"
|
||||
"mode=755"
|
||||
"size=${config.boot.runSize}"
|
||||
];
|
||||
};
|
||||
"/dev" = {
|
||||
fsType = "devtmpfs";
|
||||
options = [
|
||||
"nosuid"
|
||||
"strictatime"
|
||||
"mode=755"
|
||||
"size=${config.boot.devSize}"
|
||||
];
|
||||
};
|
||||
"/dev/shm" = {
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"nosuid"
|
||||
"nodev"
|
||||
"strictatime"
|
||||
"mode=1777"
|
||||
"size=${config.boot.devShmSize}"
|
||||
];
|
||||
};
|
||||
"/dev/pts" = {
|
||||
fsType = "devpts";
|
||||
options = [
|
||||
"nosuid"
|
||||
"noexec"
|
||||
"mode=620"
|
||||
"ptmxmode=0666"
|
||||
"gid=${toString config.ids.gids.tty}"
|
||||
];
|
||||
};
|
||||
|
||||
# To hold secrets that shouldn't be written to disk
|
||||
"/run/keys" = { fsType = "ramfs"; options = [ "nosuid" "nodev" "mode=750" ]; };
|
||||
} // optionalAttrs (!config.boot.isContainer) {
|
||||
# systemd-nspawn populates /sys by itself, and remounting it causes all
|
||||
# kinds of weird issues (most noticeably, waiting for host disk device
|
||||
# nodes).
|
||||
"/sys" = { fsType = "sysfs"; options = [ "nosuid" "noexec" "nodev" ]; };
|
||||
};
|
||||
# To hold secrets that shouldn't be written to disk
|
||||
"/run/keys" = {
|
||||
fsType = "ramfs";
|
||||
options = [
|
||||
"nosuid"
|
||||
"nodev"
|
||||
"mode=750"
|
||||
];
|
||||
};
|
||||
}
|
||||
// optionalAttrs (!config.boot.isContainer) {
|
||||
# systemd-nspawn populates /sys by itself, and remounting it causes all
|
||||
# kinds of weird issues (most noticeably, waiting for host disk device
|
||||
# nodes).
|
||||
"/sys" = {
|
||||
fsType = "sysfs";
|
||||
options = [
|
||||
"nosuid"
|
||||
"noexec"
|
||||
"nodev"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
{ config, lib, pkgs, utils, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
bootFs = lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (utils.fsNeededForBoot fs)) config.fileSystems;
|
||||
bootFs = lib.filterAttrs (
|
||||
n: fs: (fs.fsType == "bcachefs") && (utils.fsNeededForBoot fs)
|
||||
) config.fileSystems;
|
||||
|
||||
commonFunctions = ''
|
||||
prompt() {
|
||||
|
@ -57,73 +65,93 @@ let
|
|||
# bcachefs does not support mounting devices with colons in the path, ergo we don't (see #49671)
|
||||
firstDevice = fs: lib.head (lib.splitString ":" fs.device);
|
||||
|
||||
useClevis = fs: config.boot.initrd.clevis.enable && (lib.hasAttr (firstDevice fs) config.boot.initrd.clevis.devices);
|
||||
useClevis =
|
||||
fs:
|
||||
config.boot.initrd.clevis.enable
|
||||
&& (lib.hasAttr (firstDevice fs) config.boot.initrd.clevis.devices);
|
||||
|
||||
openCommand = name: fs: if useClevis fs then ''
|
||||
if clevis decrypt < /etc/clevis/${firstDevice fs}.jwe | bcachefs unlock ${firstDevice fs}
|
||||
then
|
||||
printf "unlocked ${name} using clevis\n"
|
||||
else
|
||||
printf "falling back to interactive unlocking...\n"
|
||||
tryUnlock ${name} ${firstDevice fs}
|
||||
fi
|
||||
'' else ''
|
||||
tryUnlock ${name} ${firstDevice fs}
|
||||
'';
|
||||
|
||||
mkUnits = prefix: name: fs: let
|
||||
mountUnit = "${utils.escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint))}.mount";
|
||||
device = firstDevice fs;
|
||||
deviceUnit = "${utils.escapeSystemdPath device}.device";
|
||||
in {
|
||||
name = "unlock-bcachefs-${utils.escapeSystemdPath fs.mountPoint}";
|
||||
value = {
|
||||
description = "Unlock bcachefs for ${fs.mountPoint}";
|
||||
requiredBy = [ mountUnit ];
|
||||
after = [ deviceUnit ];
|
||||
before = [ mountUnit "shutdown.target" ];
|
||||
bindsTo = [ deviceUnit ];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
unitConfig.DefaultDependencies = false;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecCondition = "${pkgs.bcachefs-tools}/bin/bcachefs unlock -c \"${device}\"";
|
||||
Restart = "on-failure";
|
||||
RestartMode = "direct";
|
||||
# Ideally, this service would lock the key on stop.
|
||||
# As is, RemainAfterExit doesn't accomplish anything.
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = let
|
||||
unlock = ''${pkgs.bcachefs-tools}/bin/bcachefs unlock "${device}"'';
|
||||
unlockInteractively = ''${config.boot.initrd.systemd.package}/bin/systemd-ask-password --timeout=0 "enter passphrase for ${name}" | exec ${unlock}'';
|
||||
in if useClevis fs then ''
|
||||
if ${config.boot.initrd.clevis.package}/bin/clevis decrypt < "/etc/clevis/${device}.jwe" | ${unlock}
|
||||
openCommand =
|
||||
name: fs:
|
||||
if useClevis fs then
|
||||
''
|
||||
if clevis decrypt < /etc/clevis/${firstDevice fs}.jwe | bcachefs unlock ${firstDevice fs}
|
||||
then
|
||||
printf "unlocked ${name} using clevis\n"
|
||||
else
|
||||
printf "falling back to interactive unlocking...\n"
|
||||
${unlockInteractively}
|
||||
tryUnlock ${name} ${firstDevice fs}
|
||||
fi
|
||||
'' else ''
|
||||
${unlockInteractively}
|
||||
''
|
||||
else
|
||||
''
|
||||
tryUnlock ${name} ${firstDevice fs}
|
||||
'';
|
||||
|
||||
mkUnits =
|
||||
prefix: name: fs:
|
||||
let
|
||||
mountUnit = "${utils.escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint))}.mount";
|
||||
device = firstDevice fs;
|
||||
deviceUnit = "${utils.escapeSystemdPath device}.device";
|
||||
in
|
||||
{
|
||||
name = "unlock-bcachefs-${utils.escapeSystemdPath fs.mountPoint}";
|
||||
value = {
|
||||
description = "Unlock bcachefs for ${fs.mountPoint}";
|
||||
requiredBy = [ mountUnit ];
|
||||
after = [ deviceUnit ];
|
||||
before = [
|
||||
mountUnit
|
||||
"shutdown.target"
|
||||
];
|
||||
bindsTo = [ deviceUnit ];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
unitConfig.DefaultDependencies = false;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecCondition = "${pkgs.bcachefs-tools}/bin/bcachefs unlock -c \"${device}\"";
|
||||
Restart = "on-failure";
|
||||
RestartMode = "direct";
|
||||
# Ideally, this service would lock the key on stop.
|
||||
# As is, RemainAfterExit doesn't accomplish anything.
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script =
|
||||
let
|
||||
unlock = ''${pkgs.bcachefs-tools}/bin/bcachefs unlock "${device}"'';
|
||||
unlockInteractively = ''${config.boot.initrd.systemd.package}/bin/systemd-ask-password --timeout=0 "enter passphrase for ${name}" | exec ${unlock}'';
|
||||
in
|
||||
if useClevis fs then
|
||||
''
|
||||
if ${config.boot.initrd.clevis.package}/bin/clevis decrypt < "/etc/clevis/${device}.jwe" | ${unlock}
|
||||
then
|
||||
printf "unlocked ${name} using clevis\n"
|
||||
else
|
||||
printf "falling back to interactive unlocking...\n"
|
||||
${unlockInteractively}
|
||||
fi
|
||||
''
|
||||
else
|
||||
''
|
||||
${unlockInteractively}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = let
|
||||
kernel = config.boot.kernelPackages.kernel;
|
||||
in (
|
||||
kernel.kernelAtLeast "6.7" || (
|
||||
lib.elem (kernel.structuredExtraConfig.BCACHEFS_FS or null) [
|
||||
assertion =
|
||||
let
|
||||
kernel = config.boot.kernelPackages.kernel;
|
||||
in
|
||||
(
|
||||
kernel.kernelAtLeast "6.7"
|
||||
|| (lib.elem (kernel.structuredExtraConfig.BCACHEFS_FS or null) [
|
||||
lib.kernel.module
|
||||
lib.kernel.yes
|
||||
(lib.kernel.option lib.kernel.yes)
|
||||
]
|
||||
)
|
||||
);
|
||||
])
|
||||
);
|
||||
|
||||
message = "Linux 6.7-rc1 at minimum or a custom linux kernel with bcachefs support is required";
|
||||
}
|
||||
|
@ -131,41 +159,52 @@ let
|
|||
in
|
||||
|
||||
{
|
||||
config = lib.mkIf (config.boot.supportedFilesystems.bcachefs or false) (lib.mkMerge [
|
||||
{
|
||||
inherit assertions;
|
||||
# needed for systemd-remount-fs
|
||||
system.fsPackages = [ pkgs.bcachefs-tools ];
|
||||
# FIXME: Remove this line when the LTS (default) kernel is at least version 6.7
|
||||
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
|
||||
services.udev.packages = [ pkgs.bcachefs-tools ];
|
||||
config = lib.mkIf (config.boot.supportedFilesystems.bcachefs or false) (
|
||||
lib.mkMerge [
|
||||
{
|
||||
inherit assertions;
|
||||
# needed for systemd-remount-fs
|
||||
system.fsPackages = [ pkgs.bcachefs-tools ];
|
||||
# FIXME: Remove this line when the LTS (default) kernel is at least version 6.7
|
||||
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
|
||||
services.udev.packages = [ pkgs.bcachefs-tools ];
|
||||
|
||||
systemd = {
|
||||
packages = [ pkgs.bcachefs-tools ];
|
||||
services = lib.mapAttrs' (mkUnits "") (lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (!utils.fsNeededForBoot fs)) config.fileSystems);
|
||||
};
|
||||
}
|
||||
systemd = {
|
||||
packages = [ pkgs.bcachefs-tools ];
|
||||
services = lib.mapAttrs' (mkUnits "") (
|
||||
lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (!utils.fsNeededForBoot fs)) config.fileSystems
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
(lib.mkIf ((config.boot.initrd.supportedFilesystems.bcachefs or false) || (bootFs != {})) {
|
||||
inherit assertions;
|
||||
# chacha20 and poly1305 are required only for decryption attempts
|
||||
boot.initrd.availableKernelModules = [ "bcachefs" "sha256" "chacha20" "poly1305" ];
|
||||
boot.initrd.systemd.extraBin = {
|
||||
# do we need this? boot/systemd.nix:566 & boot/systemd/initrd.nix:357
|
||||
"bcachefs" = "${pkgs.bcachefs-tools}/bin/bcachefs";
|
||||
"mount.bcachefs" = "${pkgs.bcachefs-tools}/bin/mount.bcachefs";
|
||||
};
|
||||
boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/bcachefs
|
||||
copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/mount.bcachefs
|
||||
'';
|
||||
boot.initrd.extraUtilsCommandsTest = lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
$out/bin/bcachefs version
|
||||
'';
|
||||
(lib.mkIf ((config.boot.initrd.supportedFilesystems.bcachefs or false) || (bootFs != { })) {
|
||||
inherit assertions;
|
||||
# chacha20 and poly1305 are required only for decryption attempts
|
||||
boot.initrd.availableKernelModules = [
|
||||
"bcachefs"
|
||||
"sha256"
|
||||
"chacha20"
|
||||
"poly1305"
|
||||
];
|
||||
boot.initrd.systemd.extraBin = {
|
||||
# do we need this? boot/systemd.nix:566 & boot/systemd/initrd.nix:357
|
||||
"bcachefs" = "${pkgs.bcachefs-tools}/bin/bcachefs";
|
||||
"mount.bcachefs" = "${pkgs.bcachefs-tools}/bin/mount.bcachefs";
|
||||
};
|
||||
boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/bcachefs
|
||||
copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/mount.bcachefs
|
||||
'';
|
||||
boot.initrd.extraUtilsCommandsTest = lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
$out/bin/bcachefs version
|
||||
'';
|
||||
|
||||
boot.initrd.postDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + lib.concatStrings (lib.mapAttrsToList openCommand bootFs));
|
||||
boot.initrd.postDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
|
||||
commonFunctions + lib.concatStrings (lib.mapAttrsToList openCommand bootFs)
|
||||
);
|
||||
|
||||
boot.initrd.systemd.services = lib.mapAttrs' (mkUnits "/sysroot") bootFs;
|
||||
})
|
||||
]);
|
||||
boot.initrd.systemd.services = lib.mapAttrs' (mkUnits "/sysroot") bootFs;
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
{ config, lib, options, pkgs, utils, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
#
|
||||
# TODO: zfs tunables
|
||||
|
||||
|
@ -13,7 +20,17 @@ let
|
|||
cfgZED = config.services.zfs.zed;
|
||||
|
||||
selectModulePackage = package: config.boot.kernelPackages.${package.kernelModuleAttribute};
|
||||
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);
|
||||
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;
|
||||
inSystem = config.boot.supportedFilesystems.zfs or false;
|
||||
|
@ -36,7 +53,13 @@ let
|
|||
|
||||
dataPools = lib.unique (lib.filter (pool: !(lib.elem pool rootPools)) allPools);
|
||||
|
||||
snapshotNames = [ "frequent" "hourly" "daily" "weekly" "monthly" ];
|
||||
snapshotNames = [
|
||||
"frequent"
|
||||
"hourly"
|
||||
"daily"
|
||||
"weekly"
|
||||
"monthly"
|
||||
];
|
||||
|
||||
# When importing ZFS pools, there's one difficulty: These scripts may run
|
||||
# before the backing devices (physical HDDs, etc.) of the pool have been
|
||||
|
@ -56,42 +79,51 @@ let
|
|||
# sufficient amount of time has passed that we can assume it won't be. In the
|
||||
# latter case it makes one last attempt at importing, allowing the system to
|
||||
# (eventually) boot even with a degraded pool.
|
||||
importLib = {zpoolCmd, awkCmd, pool}: let
|
||||
devNodes = if pool != null && cfgZfs.pools ? ${pool} then cfgZfs.pools.${pool}.devNodes else cfgZfs.devNodes;
|
||||
in ''
|
||||
# shellcheck disable=SC2013
|
||||
for o in $(cat /proc/cmdline); do
|
||||
case $o in
|
||||
zfs_force|zfs_force=1|zfs_force=y)
|
||||
ZFS_FORCE="-f"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
poolReady() {
|
||||
pool="$1"
|
||||
state="$("${zpoolCmd}" import -d "${devNodes}" 2>/dev/null | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")"
|
||||
if [[ "$state" = "ONLINE" ]]; then
|
||||
return 0
|
||||
else
|
||||
echo "Pool $pool in state $state, waiting"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
poolImported() {
|
||||
pool="$1"
|
||||
"${zpoolCmd}" list "$pool" >/dev/null 2>/dev/null
|
||||
}
|
||||
poolImport() {
|
||||
pool="$1"
|
||||
# shellcheck disable=SC2086
|
||||
"${zpoolCmd}" import -d "${devNodes}" -N $ZFS_FORCE "$pool"
|
||||
}
|
||||
'';
|
||||
importLib =
|
||||
{
|
||||
zpoolCmd,
|
||||
awkCmd,
|
||||
pool,
|
||||
}:
|
||||
let
|
||||
devNodes =
|
||||
if pool != null && cfgZfs.pools ? ${pool} then cfgZfs.pools.${pool}.devNodes else cfgZfs.devNodes;
|
||||
in
|
||||
''
|
||||
# shellcheck disable=SC2013
|
||||
for o in $(cat /proc/cmdline); do
|
||||
case $o in
|
||||
zfs_force|zfs_force=1|zfs_force=y)
|
||||
ZFS_FORCE="-f"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
poolReady() {
|
||||
pool="$1"
|
||||
state="$("${zpoolCmd}" import -d "${devNodes}" 2>/dev/null | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")"
|
||||
if [[ "$state" = "ONLINE" ]]; then
|
||||
return 0
|
||||
else
|
||||
echo "Pool $pool in state $state, waiting"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
poolImported() {
|
||||
pool="$1"
|
||||
"${zpoolCmd}" list "$pool" >/dev/null 2>/dev/null
|
||||
}
|
||||
poolImport() {
|
||||
pool="$1"
|
||||
# shellcheck disable=SC2086
|
||||
"${zpoolCmd}" import -d "${devNodes}" -N $ZFS_FORCE "$pool"
|
||||
}
|
||||
'';
|
||||
|
||||
getPoolFilesystems = pool:
|
||||
lib.filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
|
||||
getPoolFilesystems =
|
||||
pool: lib.filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
|
||||
|
||||
getPoolMounts = prefix: pool:
|
||||
getPoolMounts =
|
||||
prefix: pool:
|
||||
let
|
||||
poolFSes = getPoolFilesystems pool;
|
||||
|
||||
|
@ -102,37 +134,55 @@ let
|
|||
|
||||
hasUsr = lib.any (fs: fs.mountPoint == "/usr") poolFSes;
|
||||
in
|
||||
map (x: "${mountPoint x}.mount") poolFSes
|
||||
++ lib.optional hasUsr "sysusr-usr.mount";
|
||||
map (x: "${mountPoint x}.mount") poolFSes ++ lib.optional hasUsr "sysusr-usr.mount";
|
||||
|
||||
getKeyLocations = pool: if lib.isBool cfgZfs.requestEncryptionCredentials then {
|
||||
hasKeys = cfgZfs.requestEncryptionCredentials;
|
||||
command = "${cfgZfs.package}/sbin/zfs list -rHo name,keylocation,keystatus -t volume,filesystem ${pool}";
|
||||
} else let
|
||||
keys = lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials;
|
||||
in {
|
||||
hasKeys = keys != [];
|
||||
command = "${cfgZfs.package}/sbin/zfs list -Ho name,keylocation,keystatus -t volume,filesystem ${toString keys}";
|
||||
};
|
||||
getKeyLocations =
|
||||
pool:
|
||||
if lib.isBool cfgZfs.requestEncryptionCredentials then
|
||||
{
|
||||
hasKeys = cfgZfs.requestEncryptionCredentials;
|
||||
command = "${cfgZfs.package}/sbin/zfs list -rHo name,keylocation,keystatus -t volume,filesystem ${pool}";
|
||||
}
|
||||
else
|
||||
let
|
||||
keys = lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials;
|
||||
in
|
||||
{
|
||||
hasKeys = 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 ? "",
|
||||
}:
|
||||
lib.nameValuePair "zfs-import-${pool}" {
|
||||
description = "Import ZFS pool \"${pool}\"";
|
||||
# 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.
|
||||
# In the future, hopefully someone will complete this:
|
||||
# https://github.com/zfsonlinux/zfs/pull/4943
|
||||
wants = [ "systemd-udev-settle.service" ] ++ lib.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 = [
|
||||
"systemd-udev-settle.service"
|
||||
"systemd-modules-load.service"
|
||||
"systemd-ask-password-console.service"
|
||||
] ++ lib.optional (config.boot.initrd.clevis.useTang) "network-online.target";
|
||||
requiredBy = let
|
||||
poolFilesystems = getPoolFilesystems pool;
|
||||
noauto = poolFilesystems != [ ] && lib.all (fs: lib.elem "noauto" fs.options) poolFilesystems;
|
||||
in getPoolMounts prefix pool ++ lib.optional (!noauto) "zfs-import.target";
|
||||
before = getPoolMounts prefix pool ++ [ "shutdown.target" "zfs-import.target" ];
|
||||
requiredBy =
|
||||
let
|
||||
poolFilesystems = getPoolFilesystems pool;
|
||||
noauto = poolFilesystems != [ ] && lib.all (fs: lib.elem "noauto" fs.options) poolFilesystems;
|
||||
in
|
||||
getPoolMounts prefix pool ++ lib.optional (!noauto) "zfs-import.target";
|
||||
before = getPoolMounts prefix pool ++ [
|
||||
"shutdown.target"
|
||||
"zfs-import.target"
|
||||
];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
unitConfig = {
|
||||
DefaultDependencies = "no";
|
||||
|
@ -142,69 +192,83 @@ let
|
|||
RemainAfterExit = true;
|
||||
};
|
||||
environment.ZFS_FORCE = lib.optionalString force "-f";
|
||||
script = let
|
||||
keyLocations = getKeyLocations pool;
|
||||
in (importLib {
|
||||
# See comments at importLib definition.
|
||||
zpoolCmd = "${cfgZfs.package}/sbin/zpool";
|
||||
awkCmd = "${pkgs.gawk}/bin/awk";
|
||||
inherit pool;
|
||||
}) + ''
|
||||
if ! poolImported "${pool}"; then
|
||||
echo -n "importing ZFS pool \"${pool}\"..."
|
||||
# Loop across the import until it succeeds, because the devices needed may not be discovered yet.
|
||||
for _ in $(seq 1 60); do
|
||||
poolReady "${pool}" && poolImport "${pool}" && break
|
||||
sleep 1
|
||||
done
|
||||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
fi
|
||||
if poolImported "${pool}"; then
|
||||
${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))}
|
||||
|
||||
|
||||
${lib.optionalString keyLocations.hasKeys ''
|
||||
${keyLocations.command} | while IFS=$'\t' read -r ds kl ks; do
|
||||
{
|
||||
if [[ "$ks" != unavailable ]]; then
|
||||
continue
|
||||
fi
|
||||
case "$kl" in
|
||||
none )
|
||||
;;
|
||||
prompt )
|
||||
tries=3
|
||||
success=false
|
||||
while [[ $success != true ]] && [[ $tries -gt 0 ]]; do
|
||||
${systemd}/bin/systemd-ask-password --timeout=${toString cfgZfs.passwordTimeout} "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" \
|
||||
&& success=true \
|
||||
|| tries=$((tries - 1))
|
||||
done
|
||||
[[ $success = true ]]
|
||||
;;
|
||||
* )
|
||||
${cfgZfs.package}/sbin/zfs load-key "$ds"
|
||||
;;
|
||||
esac
|
||||
} < /dev/null # To protect while read ds kl in case anything reads stdin
|
||||
script =
|
||||
let
|
||||
keyLocations = getKeyLocations pool;
|
||||
in
|
||||
(importLib {
|
||||
# See comments at importLib definition.
|
||||
zpoolCmd = "${cfgZfs.package}/sbin/zpool";
|
||||
awkCmd = "${pkgs.gawk}/bin/awk";
|
||||
inherit pool;
|
||||
})
|
||||
+ ''
|
||||
if ! poolImported "${pool}"; then
|
||||
echo -n "importing ZFS pool \"${pool}\"..."
|
||||
# Loop across the import until it succeeds, because the devices needed may not be discovered yet.
|
||||
for _ in $(seq 1 60); do
|
||||
poolReady "${pool}" && poolImport "${pool}" && break
|
||||
sleep 1
|
||||
done
|
||||
''}
|
||||
echo "Successfully imported ${pool}"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
fi
|
||||
if poolImported "${pool}"; then
|
||||
${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)
|
||||
)}
|
||||
|
||||
|
||||
${lib.optionalString keyLocations.hasKeys ''
|
||||
${keyLocations.command} | while IFS=$'\t' read -r ds kl ks; do
|
||||
{
|
||||
if [[ "$ks" != unavailable ]]; then
|
||||
continue
|
||||
fi
|
||||
case "$kl" in
|
||||
none )
|
||||
;;
|
||||
prompt )
|
||||
tries=3
|
||||
success=false
|
||||
while [[ $success != true ]] && [[ $tries -gt 0 ]]; do
|
||||
${systemd}/bin/systemd-ask-password --timeout=${toString cfgZfs.passwordTimeout} "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" \
|
||||
&& success=true \
|
||||
|| tries=$((tries - 1))
|
||||
done
|
||||
[[ $success = true ]]
|
||||
;;
|
||||
* )
|
||||
${cfgZfs.package}/sbin/zfs load-key "$ds"
|
||||
;;
|
||||
esac
|
||||
} < /dev/null # To protect while read ds kl in case anything reads stdin
|
||||
done
|
||||
''}
|
||||
echo "Successfully imported ${pool}"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
zedConf = lib.generators.toKeyValue {
|
||||
mkKeyValue = lib.generators.mkKeyValueDefault {
|
||||
mkValueString = v:
|
||||
if lib.isInt v then toString v
|
||||
else if lib.isString v then "\"${v}\""
|
||||
else if true == v then "1"
|
||||
else if false == v then "0"
|
||||
else if lib.isList v then "\"" + (lib.concatStringsSep " " v) + "\""
|
||||
else lib.err "this value is" (toString v);
|
||||
mkValueString =
|
||||
v:
|
||||
if lib.isInt v then
|
||||
toString v
|
||||
else if lib.isString v then
|
||||
"\"${v}\""
|
||||
else if true == v then
|
||||
"1"
|
||||
else if false == v then
|
||||
"0"
|
||||
else if lib.isList v then
|
||||
"\"" + (lib.concatStringsSep " " v) + "\""
|
||||
else
|
||||
lib.err "this value is" (toString v);
|
||||
} "=";
|
||||
} cfgZED.settings;
|
||||
in
|
||||
|
@ -212,8 +276,16 @@ in
|
|||
{
|
||||
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "boot" "zfs" "enableLegacyCrypto" ] "The corresponding package was removed from nixpkgs.")
|
||||
(lib.mkRemovedOptionModule [ "boot" "zfs" "enableUnstable" ] "Instead set `boot.zfs.package = pkgs.zfs_unstable;`")
|
||||
(lib.mkRemovedOptionModule [
|
||||
"boot"
|
||||
"zfs"
|
||||
"enableLegacyCrypto"
|
||||
] "The corresponding package was removed from nixpkgs.")
|
||||
(lib.mkRemovedOptionModule [
|
||||
"boot"
|
||||
"zfs"
|
||||
"enableUnstable"
|
||||
] "Instead set `boot.zfs.package = pkgs.zfs_unstable;`")
|
||||
];
|
||||
|
||||
###### interface
|
||||
|
@ -253,8 +325,11 @@ in
|
|||
|
||||
extraPools = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
example = [ "tank" "data" ];
|
||||
default = [ ];
|
||||
example = [
|
||||
"tank"
|
||||
"data"
|
||||
];
|
||||
description = ''
|
||||
Name or GUID of extra ZFS pools that you wish to import during boot.
|
||||
|
||||
|
@ -316,7 +391,10 @@ in
|
|||
requestEncryptionCredentials = lib.mkOption {
|
||||
type = lib.types.either lib.types.bool (lib.types.listOf lib.types.str);
|
||||
default = true;
|
||||
example = [ "tank" "data" ];
|
||||
example = [
|
||||
"tank"
|
||||
"data"
|
||||
];
|
||||
description = ''
|
||||
If true on import encryption keys or passwords for all encrypted datasets
|
||||
are requested. To only decrypt selected datasets supply a list of dataset
|
||||
|
@ -336,16 +414,18 @@ in
|
|||
};
|
||||
|
||||
pools = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
devNodes = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = cfgZfs.devNodes;
|
||||
defaultText = "config.boot.zfs.devNodes";
|
||||
description = options.boot.zfs.devNodes.description;
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
devNodes = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = cfgZfs.devNodes;
|
||||
defaultText = "config.boot.zfs.devNodes";
|
||||
description = options.boot.zfs.devNodes.description;
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
default = { };
|
||||
description = ''
|
||||
Configuration for individual pools to override global defaults.
|
||||
|
@ -501,7 +581,7 @@ in
|
|||
};
|
||||
|
||||
pools = lib.mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = lib.types.listOf lib.types.str;
|
||||
example = [ "tank" ];
|
||||
description = ''
|
||||
|
@ -512,9 +592,15 @@ in
|
|||
};
|
||||
|
||||
services.zfs.expandOnBoot = lib.mkOption {
|
||||
type = lib.types.either (lib.types.enum [ "disabled" "all" ]) (lib.types.listOf lib.types.str);
|
||||
type = lib.types.either (lib.types.enum [
|
||||
"disabled"
|
||||
"all"
|
||||
]) (lib.types.listOf lib.types.str);
|
||||
default = "disabled";
|
||||
example = [ "tank" "dozer" ];
|
||||
example = [
|
||||
"tank"
|
||||
"dozer"
|
||||
];
|
||||
description = ''
|
||||
After importing, expand each device in the specified pools.
|
||||
|
||||
|
@ -541,7 +627,18 @@ in
|
|||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = let t = lib.types; in t.attrsOf (t.oneOf [ t.str t.int t.bool (t.listOf t.str) ]);
|
||||
type =
|
||||
let
|
||||
t = lib.types;
|
||||
in
|
||||
t.attrsOf (
|
||||
t.oneOf [
|
||||
t.str
|
||||
t.int
|
||||
t.bool
|
||||
(t.listOf t.str)
|
||||
]
|
||||
);
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
ZED_DEBUG_LOG = "/tmp/zed.debug.log";
|
||||
|
@ -614,62 +711,82 @@ in
|
|||
|
||||
boot.initrd = lib.mkIf inInitrd {
|
||||
kernelModules = [ "zfs" ];
|
||||
extraUtilsCommands =
|
||||
lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
copy_bin_and_libs ${cfgZfs.package}/sbin/zfs
|
||||
copy_bin_and_libs ${cfgZfs.package}/sbin/zdb
|
||||
copy_bin_and_libs ${cfgZfs.package}/sbin/zpool
|
||||
copy_bin_and_libs ${cfgZfs.package}/lib/udev/vdev_id
|
||||
copy_bin_and_libs ${cfgZfs.package}/lib/udev/zvol_id
|
||||
'';
|
||||
extraUtilsCommandsTest =
|
||||
lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
$out/bin/zfs --help >/dev/null 2>&1
|
||||
$out/bin/zpool --help >/dev/null 2>&1
|
||||
'';
|
||||
postResumeCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (lib.concatStringsSep "\n" ([''
|
||||
ZFS_FORCE="${lib.optionalString cfgZfs.forceImportRoot "-f"}"
|
||||
''] ++ [(importLib {
|
||||
# See comments at importLib definition.
|
||||
zpoolCmd = "zpool";
|
||||
awkCmd = "awk";
|
||||
pool = null;
|
||||
})] ++ (map (pool: ''
|
||||
echo -n "importing root ZFS pool \"${pool}\"..."
|
||||
# Loop across the import until it succeeds, because the devices needed may not be discovered yet.
|
||||
if ! poolImported "${pool}"; then
|
||||
for _ in $(seq 1 60); do
|
||||
poolReady "${pool}" > /dev/null && msg="$(poolImport "${pool}" 2>&1)" && break
|
||||
sleep 1
|
||||
echo -n .
|
||||
done
|
||||
echo
|
||||
if [[ -n "$msg" ]]; then
|
||||
echo "$msg";
|
||||
fi
|
||||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
fi
|
||||
|
||||
${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 lib.isBool cfgZfs.requestEncryptionCredentials
|
||||
then lib.optionalString cfgZfs.requestEncryptionCredentials ''
|
||||
zfs load-key -a
|
||||
extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
copy_bin_and_libs ${cfgZfs.package}/sbin/zfs
|
||||
copy_bin_and_libs ${cfgZfs.package}/sbin/zdb
|
||||
copy_bin_and_libs ${cfgZfs.package}/sbin/zpool
|
||||
copy_bin_and_libs ${cfgZfs.package}/lib/udev/vdev_id
|
||||
copy_bin_and_libs ${cfgZfs.package}/lib/udev/zvol_id
|
||||
'';
|
||||
extraUtilsCommandsTest = lib.mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
$out/bin/zfs --help >/dev/null 2>&1
|
||||
$out/bin/zpool --help >/dev/null 2>&1
|
||||
'';
|
||||
postResumeCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
|
||||
lib.concatStringsSep "\n" (
|
||||
[
|
||||
''
|
||||
else lib.concatMapStrings (fs: ''
|
||||
zfs load-key -- ${lib.escapeShellArg fs}
|
||||
'') (lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)}
|
||||
'') rootPools)));
|
||||
ZFS_FORCE="${lib.optionalString cfgZfs.forceImportRoot "-f"}"
|
||||
''
|
||||
]
|
||||
++ [
|
||||
(importLib {
|
||||
# See comments at importLib definition.
|
||||
zpoolCmd = "zpool";
|
||||
awkCmd = "awk";
|
||||
pool = null;
|
||||
})
|
||||
]
|
||||
++ (map (pool: ''
|
||||
echo -n "importing root ZFS pool \"${pool}\"..."
|
||||
# Loop across the import until it succeeds, because the devices needed may not be discovered yet.
|
||||
if ! poolImported "${pool}"; then
|
||||
for _ in $(seq 1 60); do
|
||||
poolReady "${pool}" > /dev/null && msg="$(poolImport "${pool}" 2>&1)" && break
|
||||
sleep 1
|
||||
echo -n .
|
||||
done
|
||||
echo
|
||||
if [[ -n "$msg" ]]; then
|
||||
echo "$msg";
|
||||
fi
|
||||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
fi
|
||||
|
||||
${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 lib.isBool cfgZfs.requestEncryptionCredentials then
|
||||
lib.optionalString cfgZfs.requestEncryptionCredentials ''
|
||||
zfs load-key -a
|
||||
''
|
||||
else
|
||||
lib.concatMapStrings (fs: ''
|
||||
zfs load-key -- ${lib.escapeShellArg fs}
|
||||
'') (lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)
|
||||
}
|
||||
'') rootPools)
|
||||
)
|
||||
);
|
||||
|
||||
# Systemd in stage 1
|
||||
systemd = lib.mkIf config.boot.initrd.systemd.enable {
|
||||
packages = [cfgZfs.package];
|
||||
services = lib.listToAttrs (map (pool: createImportService {
|
||||
inherit pool;
|
||||
systemd = config.boot.initrd.systemd.package;
|
||||
force = cfgZfs.forceImportRoot;
|
||||
prefix = "/sysroot";
|
||||
}) rootPools);
|
||||
packages = [ cfgZfs.package ];
|
||||
services = lib.listToAttrs (
|
||||
map (
|
||||
pool:
|
||||
createImportService {
|
||||
inherit pool;
|
||||
systemd = config.boot.initrd.systemd.package;
|
||||
force = cfgZfs.forceImportRoot;
|
||||
prefix = "/sysroot";
|
||||
}
|
||||
) rootPools
|
||||
);
|
||||
targets.zfs-import.wantedBy = [ "zfs.target" ];
|
||||
targets.zfs.wantedBy = [ "initrd.target" ];
|
||||
extraBin = {
|
||||
|
@ -682,13 +799,14 @@ in
|
|||
"${cfgZfs.package}/lib/udev/zvol_id"
|
||||
];
|
||||
};
|
||||
services.udev.packages = [cfgZfs.package]; # to hook zvol naming, in stage 1
|
||||
services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, in stage 1
|
||||
};
|
||||
|
||||
systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/zpool".source = pkgs.writeShellScript "zpool-sync-shutdown" ''
|
||||
exec ${cfgZfs.package}/bin/zpool sync
|
||||
'';
|
||||
systemd.shutdownRamfs.storePaths = ["${cfgZfs.package}/bin/zpool"];
|
||||
systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/zpool".source =
|
||||
pkgs.writeShellScript "zpool-sync-shutdown" ''
|
||||
exec ${cfgZfs.package}/bin/zpool sync
|
||||
'';
|
||||
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.
|
||||
boot.loader.grub = lib.mkIf (inInitrd || inSystem) {
|
||||
|
@ -697,10 +815,11 @@ in
|
|||
};
|
||||
|
||||
services.zfs.zed.settings = {
|
||||
ZED_EMAIL_PROG = lib.mkIf cfgZED.enableMail (lib.mkDefault (
|
||||
config.security.wrapperDir + "/" +
|
||||
config.services.mail.sendmailSetuidWrapper.program
|
||||
));
|
||||
ZED_EMAIL_PROG = lib.mkIf cfgZED.enableMail (
|
||||
lib.mkDefault (
|
||||
config.security.wrapperDir + "/" + config.services.mail.sendmailSetuidWrapper.program
|
||||
)
|
||||
);
|
||||
# subject in header for sendmail
|
||||
ZED_EMAIL_OPTS = lib.mkIf cfgZED.enableMail (lib.mkDefault "@ADDRESS@");
|
||||
|
||||
|
@ -721,10 +840,9 @@ 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"
|
||||
'';
|
||||
|
||||
environment.etc = lib.genAttrs
|
||||
(map
|
||||
(file: "zfs/zed.d/${file}")
|
||||
[
|
||||
environment.etc =
|
||||
lib.genAttrs
|
||||
(map (file: "zfs/zed.d/${file}") [
|
||||
"all-syslog.sh"
|
||||
"pool_import-led.sh"
|
||||
"resilver_finish-start-scrub.sh"
|
||||
|
@ -736,56 +854,68 @@ in
|
|||
"scrub_finish-notify.sh"
|
||||
"statechange-notify.sh"
|
||||
"vdev_clear-led.sh"
|
||||
]
|
||||
)
|
||||
(file: { source = "${cfgZfs.package}/etc/${file}"; })
|
||||
// {
|
||||
"zfs/zed.d/zed.rc".text = zedConf;
|
||||
"zfs/zpool.d".source = "${cfgZfs.package}/etc/zfs/zpool.d/";
|
||||
};
|
||||
])
|
||||
(file: {
|
||||
source = "${cfgZfs.package}/etc/${file}";
|
||||
})
|
||||
// {
|
||||
"zfs/zed.d/zed.rc".text = zedConf;
|
||||
"zfs/zpool.d".source = "${cfgZfs.package}/etc/zfs/zpool.d/";
|
||||
};
|
||||
|
||||
system.fsPackages = [ cfgZfs.package ]; # XXX: needed? zfs doesn't have (need) a fsck
|
||||
environment.systemPackages = [ cfgZfs.package ]
|
||||
++ lib.optional cfgSnapshots.enable autosnapPkg; # so the user can run the command to see flags
|
||||
environment.systemPackages = [ cfgZfs.package ] ++ 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.
|
||||
systemd.packages = [ cfgZfs.package ];
|
||||
|
||||
systemd.services = let
|
||||
createImportService' = pool: createImportService {
|
||||
inherit pool;
|
||||
systemd = config.systemd.package;
|
||||
force = cfgZfs.forceImportAll;
|
||||
};
|
||||
|
||||
# This forces a sync of any ZFS pools prior to poweroff, even if they're set
|
||||
# to sync=disabled.
|
||||
createSyncService = pool:
|
||||
lib.nameValuePair "zfs-sync-${pool}" {
|
||||
description = "Sync ZFS pool \"${pool}\"";
|
||||
wantedBy = [ "shutdown.target" ];
|
||||
before = [ "final.target" ];
|
||||
unitConfig = {
|
||||
DefaultDependencies = false;
|
||||
systemd.services =
|
||||
let
|
||||
createImportService' =
|
||||
pool:
|
||||
createImportService {
|
||||
inherit pool;
|
||||
systemd = config.systemd.package;
|
||||
force = cfgZfs.forceImportAll;
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
|
||||
# This forces a sync of any ZFS pools prior to poweroff, even if they're set
|
||||
# to sync=disabled.
|
||||
createSyncService =
|
||||
pool:
|
||||
lib.nameValuePair "zfs-sync-${pool}" {
|
||||
description = "Sync ZFS pool \"${pool}\"";
|
||||
wantedBy = [ "shutdown.target" ];
|
||||
before = [ "final.target" ];
|
||||
unitConfig = {
|
||||
DefaultDependencies = false;
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
${cfgZfs.package}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
|
||||
'';
|
||||
};
|
||||
script = ''
|
||||
${cfgZfs.package}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
|
||||
'';
|
||||
};
|
||||
|
||||
createZfsService = serv:
|
||||
lib.nameValuePair serv {
|
||||
after = [ "systemd-modules-load.service" ];
|
||||
wantedBy = [ "zfs.target" ];
|
||||
};
|
||||
createZfsService =
|
||||
serv:
|
||||
lib.nameValuePair serv {
|
||||
after = [ "systemd-modules-load.service" ];
|
||||
wantedBy = [ "zfs.target" ];
|
||||
};
|
||||
|
||||
in lib.listToAttrs (map createImportService' dataPools ++
|
||||
map createSyncService allPools ++
|
||||
map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
|
||||
in
|
||||
lib.listToAttrs (
|
||||
map createImportService' dataPools
|
||||
++ map createSyncService allPools
|
||||
++ map createZfsService [
|
||||
"zfs-mount"
|
||||
"zfs-share"
|
||||
"zfs-zed"
|
||||
]
|
||||
);
|
||||
|
||||
systemd.targets.zfs-import.wantedBy = [ "zfs.target" ];
|
||||
|
||||
|
@ -805,7 +935,7 @@ in
|
|||
scriptArgs = "%i";
|
||||
path = [ cfgZfs.package ];
|
||||
|
||||
script = ''
|
||||
script = ''
|
||||
pool=$1
|
||||
|
||||
echo "Expanding all devices for $pool."
|
||||
|
@ -821,9 +951,11 @@ in
|
|||
# If the `pools` option is `true`, we want to dynamically
|
||||
# expand every pool. Otherwise we want to enumerate
|
||||
# just the specifically provided list of pools.
|
||||
poolListProvider = if cfgExpandOnBoot == "all"
|
||||
then "$(zpool list -H -o name)"
|
||||
else lib.escapeShellArgs cfgExpandOnBoot;
|
||||
poolListProvider =
|
||||
if cfgExpandOnBoot == "all" then
|
||||
"$(zpool list -H -o name)"
|
||||
else
|
||||
lib.escapeShellArgs cfgExpandOnBoot;
|
||||
in
|
||||
{
|
||||
description = "Expand specified ZFS pools";
|
||||
|
@ -846,41 +978,55 @@ in
|
|||
})
|
||||
|
||||
(lib.mkIf (cfgZfs.enabled && cfgSnapshots.enable) {
|
||||
systemd.services = let
|
||||
descr = name: if name == "frequent" then "15 mins"
|
||||
else if name == "hourly" then "hour"
|
||||
else if name == "daily" then "day"
|
||||
else if name == "weekly" then "week"
|
||||
else if name == "monthly" then "month"
|
||||
else throw "unknown snapshot name";
|
||||
numSnapshots = name: builtins.getAttr name cfgSnapshots;
|
||||
in builtins.listToAttrs (map (snapName:
|
||||
{
|
||||
name = "zfs-snapshot-${snapName}";
|
||||
value = {
|
||||
description = "ZFS auto-snapshotting every ${descr snapName}";
|
||||
after = [ "zfs-import.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} ${snapName} ${toString (numSnapshots snapName)}";
|
||||
};
|
||||
restartIfChanged = false;
|
||||
};
|
||||
}) snapshotNames);
|
||||
systemd.services =
|
||||
let
|
||||
descr =
|
||||
name:
|
||||
if name == "frequent" then
|
||||
"15 mins"
|
||||
else if name == "hourly" then
|
||||
"hour"
|
||||
else if name == "daily" then
|
||||
"day"
|
||||
else if name == "weekly" then
|
||||
"week"
|
||||
else if name == "monthly" then
|
||||
"month"
|
||||
else
|
||||
throw "unknown snapshot name";
|
||||
numSnapshots = name: builtins.getAttr name cfgSnapshots;
|
||||
in
|
||||
builtins.listToAttrs (
|
||||
map (snapName: {
|
||||
name = "zfs-snapshot-${snapName}";
|
||||
value = {
|
||||
description = "ZFS auto-snapshotting every ${descr snapName}";
|
||||
after = [ "zfs-import.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} ${snapName} ${toString (numSnapshots snapName)}";
|
||||
};
|
||||
restartIfChanged = false;
|
||||
};
|
||||
}) snapshotNames
|
||||
);
|
||||
|
||||
systemd.timers = let
|
||||
timer = name: if name == "frequent" then "*:0,15,30,45" else name;
|
||||
in builtins.listToAttrs (map (snapName:
|
||||
{
|
||||
name = "zfs-snapshot-${snapName}";
|
||||
value = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = timer snapName;
|
||||
Persistent = lib.mkDefault "yes";
|
||||
};
|
||||
};
|
||||
}) snapshotNames);
|
||||
systemd.timers =
|
||||
let
|
||||
timer = name: if name == "frequent" then "*:0,15,30,45" else name;
|
||||
in
|
||||
builtins.listToAttrs (
|
||||
map (snapName: {
|
||||
name = "zfs-snapshot-${snapName}";
|
||||
value = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = timer snapName;
|
||||
Persistent = lib.mkDefault "yes";
|
||||
};
|
||||
};
|
||||
}) snapshotNames
|
||||
);
|
||||
})
|
||||
|
||||
(lib.mkIf (cfgZfs.enabled && cfgScrub.enable) {
|
||||
|
@ -894,11 +1040,11 @@ in
|
|||
script = ''
|
||||
# shellcheck disable=SC2046
|
||||
${cfgZfs.package}/bin/zpool scrub -w ${
|
||||
if cfgScrub.pools != [] then
|
||||
if cfgScrub.pools != [ ] then
|
||||
(lib.concatStringsSep " " cfgScrub.pools)
|
||||
else
|
||||
"$(${cfgZfs.package}/bin/zpool list -H -o name)"
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue