2020-06-04 21:42:16 +00:00
|
|
|
{ pkgs, config, lib, ... }:
|
|
|
|
|
|
|
|
let
|
2022-01-15 16:12:17 +00:00
|
|
|
inherit (lib) attrNames attrValues zipAttrsWith flatten mkOption
|
|
|
|
types foldl' unique noDepEntry concatMapStrings listToAttrs
|
|
|
|
escapeShellArg escapeShellArgs replaceStrings recursiveUpdate all
|
2022-01-16 17:59:03 +00:00
|
|
|
filter filterAttrs concatStringsSep concatMapStringsSep isString
|
2022-02-13 22:25:51 +00:00
|
|
|
catAttrs optional literalExpression;
|
2022-01-15 16:12:17 +00:00
|
|
|
|
|
|
|
inherit (pkgs.callPackage ./lib.nix { }) splitPath dirListToPath
|
2022-01-16 17:56:15 +00:00
|
|
|
concatPaths sanitizeName duplicates;
|
2022-01-15 16:12:17 +00:00
|
|
|
|
2020-06-04 21:42:16 +00:00
|
|
|
cfg = config.environment.persistence;
|
2022-01-28 11:58:14 +00:00
|
|
|
users = config.users.users;
|
2022-01-31 19:32:01 +00:00
|
|
|
allPersistentStoragePaths = { directories = [ ]; files = [ ]; users = [ ]; }
|
|
|
|
// (zipAttrsWith (_name: flatten) (attrValues cfg));
|
2022-01-15 16:12:17 +00:00
|
|
|
inherit (allPersistentStoragePaths) files directories;
|
2021-08-29 18:02:47 +00:00
|
|
|
mkMountScript = mountPoint: targetFile: ''
|
|
|
|
if [[ -L ${mountPoint} && $(readlink -f ${mountPoint}) == ${targetFile} ]]; then
|
|
|
|
echo "${mountPoint} already links to ${targetFile}, ignoring"
|
|
|
|
elif mount | grep -F ${mountPoint}' ' >/dev/null && ! mount | grep -F ${mountPoint}/ >/dev/null; then
|
|
|
|
echo "mount already exists at ${mountPoint}, ignoring"
|
|
|
|
elif [[ -e ${mountPoint} ]]; then
|
|
|
|
echo "A file already exists at ${mountPoint}!" >&2
|
|
|
|
exit 1
|
|
|
|
elif [[ -e ${targetFile} ]]; then
|
|
|
|
touch ${mountPoint}
|
|
|
|
mount -o bind ${targetFile} ${mountPoint}
|
|
|
|
else
|
|
|
|
ln -s ${targetFile} ${mountPoint}
|
|
|
|
fi
|
|
|
|
'';
|
2020-06-04 21:42:16 +00:00
|
|
|
in
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
|
|
|
|
environment.persistence = mkOption {
|
|
|
|
default = { };
|
2022-01-15 16:12:17 +00:00
|
|
|
type =
|
|
|
|
let
|
2022-02-04 02:40:08 +00:00
|
|
|
inherit (types) attrsOf bool listOf submodule path either str;
|
2022-01-15 16:12:17 +00:00
|
|
|
in
|
|
|
|
attrsOf (
|
|
|
|
submodule (
|
2022-01-16 17:59:03 +00:00
|
|
|
{ name, config, ... }:
|
2022-01-15 16:12:17 +00:00
|
|
|
let
|
|
|
|
persistentStoragePath = name;
|
2022-01-28 11:58:14 +00:00
|
|
|
defaultPerms = {
|
|
|
|
mode = "0755";
|
|
|
|
user = "root";
|
|
|
|
group = "root";
|
|
|
|
};
|
2022-01-15 16:12:17 +00:00
|
|
|
commonOpts = {
|
|
|
|
options = {
|
|
|
|
persistentStoragePath = mkOption {
|
|
|
|
type = path;
|
|
|
|
default = persistentStoragePath;
|
|
|
|
description = ''
|
|
|
|
The path to persistent storage where the real
|
|
|
|
file should be stored.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
2020-06-04 21:42:16 +00:00
|
|
|
};
|
2022-01-28 11:58:14 +00:00
|
|
|
dirPermsOpts = { user, group, mode }: {
|
|
|
|
user = mkOption {
|
|
|
|
type = str;
|
|
|
|
default = user;
|
|
|
|
description = ''
|
|
|
|
If the directory doesn't exist in persistent
|
|
|
|
storage it will be created and owned by the user
|
|
|
|
specified by this option.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
group = mkOption {
|
|
|
|
type = str;
|
|
|
|
default = group;
|
|
|
|
description = ''
|
|
|
|
If the directory doesn't exist in persistent
|
|
|
|
storage it will be created and owned by the
|
|
|
|
group specified by this option.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
mode = mkOption {
|
|
|
|
type = str;
|
|
|
|
default = mode;
|
|
|
|
example = "0700";
|
|
|
|
description = ''
|
|
|
|
If the directory doesn't exist in persistent
|
|
|
|
storage it will be created with the mode
|
|
|
|
specified by this option.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
fileOpts = perms: {
|
2022-01-15 16:12:17 +00:00
|
|
|
options = {
|
|
|
|
file = mkOption {
|
|
|
|
type = str;
|
|
|
|
description = ''
|
|
|
|
The path to the file.
|
|
|
|
'';
|
|
|
|
};
|
2022-01-28 11:58:14 +00:00
|
|
|
parentDirectory = dirPermsOpts perms;
|
2022-01-15 16:12:17 +00:00
|
|
|
};
|
2020-06-04 21:42:16 +00:00
|
|
|
};
|
2022-01-28 11:58:14 +00:00
|
|
|
dirOpts = perms: {
|
2022-01-15 16:12:17 +00:00
|
|
|
options = {
|
|
|
|
directory = mkOption {
|
|
|
|
type = str;
|
|
|
|
description = ''
|
|
|
|
The path to the directory.
|
|
|
|
'';
|
|
|
|
};
|
2022-01-28 11:58:14 +00:00
|
|
|
} // (dirPermsOpts perms);
|
2022-01-15 16:12:17 +00:00
|
|
|
};
|
2022-01-28 11:58:14 +00:00
|
|
|
rootFile = submodule [
|
|
|
|
commonOpts
|
|
|
|
(fileOpts defaultPerms)
|
|
|
|
];
|
|
|
|
rootDir = submodule [
|
|
|
|
commonOpts
|
|
|
|
(dirOpts defaultPerms)
|
|
|
|
];
|
2022-01-15 16:12:17 +00:00
|
|
|
in
|
|
|
|
{
|
|
|
|
options =
|
|
|
|
{
|
2022-01-16 17:59:03 +00:00
|
|
|
users = mkOption {
|
|
|
|
type = attrsOf (
|
|
|
|
submodule (
|
2022-01-28 11:58:14 +00:00
|
|
|
{ name, config, ... }:
|
|
|
|
let
|
|
|
|
userDefaultPerms = {
|
|
|
|
inherit (defaultPerms) mode;
|
|
|
|
user = name;
|
|
|
|
group = users.${userDefaultPerms.user}.group;
|
|
|
|
};
|
|
|
|
userFile = submodule [
|
|
|
|
commonOpts
|
|
|
|
(fileOpts userDefaultPerms)
|
|
|
|
];
|
|
|
|
userDir = submodule [
|
|
|
|
commonOpts
|
|
|
|
(dirOpts userDefaultPerms)
|
|
|
|
];
|
|
|
|
in
|
|
|
|
{
|
2022-01-16 17:59:03 +00:00
|
|
|
options =
|
|
|
|
{
|
|
|
|
# Needed because defining fileSystems
|
|
|
|
# based on values from users.users
|
|
|
|
# results in infinite recursion.
|
|
|
|
home = mkOption {
|
|
|
|
type = path;
|
2022-01-28 11:58:14 +00:00
|
|
|
default = "/home/${userDefaultPerms.user}";
|
2022-01-16 17:59:03 +00:00
|
|
|
defaultText = "/home/<username>";
|
|
|
|
description = ''
|
|
|
|
The user's home directory. Only
|
|
|
|
useful for users with a custom home
|
|
|
|
directory path.
|
|
|
|
|
|
|
|
Cannot currently be automatically
|
|
|
|
deduced due to a limitation in
|
|
|
|
nixpkgs.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
files = mkOption {
|
2022-01-28 11:58:14 +00:00
|
|
|
type = listOf (either str userFile);
|
2022-01-16 17:59:03 +00:00
|
|
|
default = [ ];
|
|
|
|
example = [
|
|
|
|
".screenrc"
|
|
|
|
];
|
|
|
|
description = ''
|
|
|
|
Files that should be stored in
|
|
|
|
persistent storage.
|
|
|
|
'';
|
|
|
|
apply =
|
|
|
|
map (file:
|
|
|
|
if isString file then
|
|
|
|
{
|
2022-02-02 09:03:19 +00:00
|
|
|
inherit persistentStoragePath;
|
2022-01-16 17:59:03 +00:00
|
|
|
file = concatPaths [ config.home file ];
|
2022-02-02 09:03:19 +00:00
|
|
|
parentDirectory = userDefaultPerms;
|
2022-01-16 17:59:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
file // {
|
|
|
|
file = concatPaths [ config.home file.file ];
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
directories = mkOption {
|
2022-01-28 11:58:14 +00:00
|
|
|
type = listOf (either str userDir);
|
2022-01-16 17:59:03 +00:00
|
|
|
default = [ ];
|
|
|
|
example = [
|
|
|
|
"Downloads"
|
|
|
|
"Music"
|
|
|
|
"Pictures"
|
|
|
|
"Documents"
|
|
|
|
"Videos"
|
|
|
|
];
|
|
|
|
description = ''
|
|
|
|
Directories to bind mount to
|
|
|
|
persistent storage.
|
|
|
|
'';
|
|
|
|
apply =
|
|
|
|
map (directory:
|
|
|
|
if isString directory then
|
2022-02-02 09:03:19 +00:00
|
|
|
userDefaultPerms // {
|
2022-01-16 17:59:03 +00:00
|
|
|
directory = concatPaths [ config.home directory ];
|
2022-02-02 09:03:19 +00:00
|
|
|
inherit persistentStoragePath;
|
2022-01-16 17:59:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
directory // {
|
|
|
|
directory = concatPaths [ config.home directory.directory ];
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
)
|
|
|
|
);
|
|
|
|
default = { };
|
2022-02-13 22:25:51 +00:00
|
|
|
description = ''
|
|
|
|
A set of user submodules listing the files and
|
|
|
|
directories to link to their respective user's
|
|
|
|
home directories.
|
|
|
|
|
|
|
|
Each attribute name should be the name of the
|
|
|
|
user.
|
|
|
|
|
|
|
|
For detailed usage, check the <link
|
|
|
|
xlink:href="https://github.com/nix-community/impermanence">documentation</link>.
|
|
|
|
'';
|
|
|
|
example = literalExpression ''
|
|
|
|
{
|
|
|
|
talyz = {
|
|
|
|
directories = [
|
|
|
|
"Downloads"
|
|
|
|
"Music"
|
|
|
|
"Pictures"
|
|
|
|
"Documents"
|
|
|
|
"Videos"
|
|
|
|
"VirtualBox VMs"
|
|
|
|
{ directory = ".gnupg"; mode = "0700"; }
|
|
|
|
{ directory = ".ssh"; mode = "0700"; }
|
|
|
|
{ directory = ".nixops"; mode = "0700"; }
|
|
|
|
{ directory = ".local/share/keyrings"; mode = "0700"; }
|
|
|
|
".local/share/direnv"
|
|
|
|
];
|
|
|
|
files = [
|
|
|
|
".screenrc"
|
|
|
|
];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
'';
|
2022-01-16 17:59:03 +00:00
|
|
|
};
|
|
|
|
|
2022-01-15 16:12:17 +00:00
|
|
|
files = mkOption {
|
2022-01-28 11:58:14 +00:00
|
|
|
type = listOf (either str rootFile);
|
2022-01-15 16:12:17 +00:00
|
|
|
default = [ ];
|
|
|
|
example = [
|
|
|
|
"/etc/machine-id"
|
|
|
|
"/etc/nix/id_rsa"
|
|
|
|
];
|
|
|
|
description = ''
|
|
|
|
Files that should be stored in persistent storage.
|
|
|
|
'';
|
|
|
|
apply =
|
|
|
|
map (file:
|
|
|
|
if isString file then
|
|
|
|
{
|
|
|
|
inherit file persistentStoragePath;
|
2022-01-28 11:58:14 +00:00
|
|
|
parentDirectory = defaultPerms;
|
2022-01-15 16:12:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
file);
|
|
|
|
};
|
|
|
|
|
|
|
|
directories = mkOption {
|
2022-01-28 11:58:14 +00:00
|
|
|
type = listOf (either str rootDir);
|
2022-01-15 16:12:17 +00:00
|
|
|
default = [ ];
|
|
|
|
example = [
|
|
|
|
"/var/log"
|
|
|
|
"/var/lib/bluetooth"
|
|
|
|
"/var/lib/systemd/coredump"
|
|
|
|
"/etc/NetworkManager/system-connections"
|
|
|
|
];
|
|
|
|
description = ''
|
|
|
|
Directories to bind mount to persistent storage.
|
|
|
|
'';
|
|
|
|
apply =
|
|
|
|
map (directory:
|
|
|
|
if isString directory then
|
2022-02-02 09:03:19 +00:00
|
|
|
defaultPerms // {
|
2022-01-15 16:12:17 +00:00
|
|
|
inherit directory persistentStoragePath;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
directory);
|
|
|
|
};
|
2022-02-04 02:40:08 +00:00
|
|
|
|
|
|
|
hideMounts = mkOption {
|
|
|
|
type = bool;
|
|
|
|
default = false;
|
|
|
|
example = true;
|
|
|
|
description = ''
|
|
|
|
Whether to hide bind mounts from showing up as mounted drives.
|
|
|
|
'';
|
|
|
|
};
|
2022-01-15 16:12:17 +00:00
|
|
|
};
|
2022-01-16 17:59:03 +00:00
|
|
|
config =
|
|
|
|
let
|
|
|
|
allUsers = zipAttrsWith (_name: flatten) (attrValues config.users);
|
|
|
|
in
|
|
|
|
{
|
|
|
|
files = allUsers.files or [ ];
|
|
|
|
directories = allUsers.directories or [ ];
|
|
|
|
};
|
2022-01-15 16:12:17 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
);
|
2020-12-13 15:16:35 +00:00
|
|
|
description = ''
|
2022-02-13 22:25:51 +00:00
|
|
|
A set of persistent storage location submodules listing the
|
|
|
|
files and directories to link to their respective persistent
|
|
|
|
storage location.
|
|
|
|
|
|
|
|
Each attribute name should be the full path to a persistent
|
|
|
|
storage location.
|
2020-12-13 15:16:35 +00:00
|
|
|
|
|
|
|
For detailed usage, check the <link
|
|
|
|
xlink:href="https://github.com/nix-community/impermanence">documentation</link>.
|
|
|
|
'';
|
2022-02-13 22:25:51 +00:00
|
|
|
example = literalExpression ''
|
|
|
|
{
|
|
|
|
"/persistent" = {
|
|
|
|
directories = [
|
|
|
|
"/var/log"
|
|
|
|
"/var/lib/bluetooth"
|
|
|
|
"/var/lib/systemd/coredump"
|
|
|
|
"/etc/NetworkManager/system-connections"
|
|
|
|
{ directory = "/var/lib/colord"; user = "colord"; group = "colord"; mode = "u=rwx,g=rx,o="; }
|
|
|
|
];
|
|
|
|
files = [
|
|
|
|
"/etc/machine-id"
|
|
|
|
{ file = "/etc/nix/id_rsa"; parentDirectory = { mode = "u=rwx,g=,o="; }; }
|
|
|
|
];
|
|
|
|
};
|
|
|
|
users.talyz = { ... }; # See the dedicated example
|
|
|
|
}
|
|
|
|
'';
|
2020-06-04 21:42:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
config = {
|
2021-08-29 18:02:47 +00:00
|
|
|
systemd.services =
|
2020-06-04 21:42:16 +00:00
|
|
|
let
|
2022-01-28 11:58:14 +00:00
|
|
|
mkPersistFileService = { file, persistentStoragePath, ... }:
|
2021-08-29 18:02:47 +00:00
|
|
|
let
|
|
|
|
targetFile = escapeShellArg (concatPaths [ persistentStoragePath file ]);
|
|
|
|
mountPoint = escapeShellArg file;
|
|
|
|
in
|
|
|
|
{
|
|
|
|
"persist-${sanitizeName targetFile}" = {
|
|
|
|
description = "Bind mount or link ${targetFile} to ${mountPoint}";
|
|
|
|
wantedBy = [ "local-fs.target" ];
|
|
|
|
before = [ "local-fs.target" ];
|
2022-01-31 02:22:23 +00:00
|
|
|
path = [ pkgs.util-linux ];
|
2021-08-29 18:02:47 +00:00
|
|
|
unitConfig.DefaultDependencies = false;
|
|
|
|
serviceConfig = {
|
|
|
|
Type = "oneshot";
|
|
|
|
RemainAfterExit = true;
|
|
|
|
ExecStart = pkgs.writeShellScript "bindOrLink-${sanitizeName targetFile}" ''
|
|
|
|
set -eu
|
|
|
|
${mkMountScript mountPoint targetFile}
|
|
|
|
'';
|
|
|
|
ExecStop = pkgs.writeShellScript "unbindOrUnlink-${sanitizeName targetFile}" ''
|
|
|
|
set -eu
|
|
|
|
if [[ -L ${mountPoint} ]]; then
|
|
|
|
rm ${mountPoint}
|
|
|
|
else
|
|
|
|
umount ${mountPoint}
|
|
|
|
rm ${mountPoint}
|
|
|
|
fi
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
2020-06-04 21:42:16 +00:00
|
|
|
in
|
2022-01-15 16:12:17 +00:00
|
|
|
foldl' recursiveUpdate { } (map mkPersistFileService files);
|
2020-06-04 21:42:16 +00:00
|
|
|
|
|
|
|
fileSystems =
|
|
|
|
let
|
2020-06-07 08:43:44 +00:00
|
|
|
# Create fileSystems bind mount entry.
|
2022-01-28 11:58:14 +00:00
|
|
|
mkBindMountNameValuePair = { directory, persistentStoragePath, ... }: {
|
2022-01-15 16:12:17 +00:00
|
|
|
name = concatPaths [ "/" directory ];
|
2020-06-04 21:42:16 +00:00
|
|
|
value = {
|
2022-01-15 16:12:17 +00:00
|
|
|
device = concatPaths [ persistentStoragePath directory ];
|
2020-06-04 21:42:16 +00:00
|
|
|
noCheck = true;
|
2022-02-04 02:40:08 +00:00
|
|
|
options = [ "bind" ]
|
|
|
|
++ optional cfg.${persistentStoragePath}.hideMounts "x-gvfs-hide";
|
2020-06-04 21:42:16 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-06-07 08:43:44 +00:00
|
|
|
# Create all fileSystems bind mount entries for a specific
|
|
|
|
# persistent storage path.
|
2020-06-04 21:42:16 +00:00
|
|
|
in
|
2022-01-15 16:12:17 +00:00
|
|
|
listToAttrs (map mkBindMountNameValuePair directories);
|
2020-06-04 21:42:16 +00:00
|
|
|
|
|
|
|
system.activationScripts =
|
|
|
|
let
|
2020-12-13 13:15:58 +00:00
|
|
|
# Script to create directories in persistent and ephemeral
|
|
|
|
# storage. The directory structure's mode and ownership mirror
|
|
|
|
# those of persistentStoragePath/dir.
|
|
|
|
createDirectories = pkgs.runCommand "impermanence-create-directories" { } ''
|
|
|
|
cp ${./create-directories.bash} $out
|
|
|
|
patchShebangs $out
|
|
|
|
'';
|
2020-06-04 21:42:16 +00:00
|
|
|
|
2022-01-28 11:58:14 +00:00
|
|
|
mkDirWithPerms = { directory, persistentStoragePath, user, group, mode }: ''
|
|
|
|
${createDirectories} ${escapeShellArgs [persistentStoragePath directory user group mode]}
|
2020-11-12 09:55:46 +00:00
|
|
|
'';
|
|
|
|
|
2020-06-07 08:43:44 +00:00
|
|
|
# Build an activation script which creates all persistent
|
|
|
|
# storage directories we want to bind mount.
|
2022-01-15 16:12:17 +00:00
|
|
|
dirCreationScripts =
|
2020-12-13 13:45:29 +00:00
|
|
|
let
|
2022-01-15 16:12:17 +00:00
|
|
|
inherit directories;
|
|
|
|
fileDirectories = unique (map
|
|
|
|
(f:
|
|
|
|
{
|
|
|
|
directory = dirOf f.file;
|
|
|
|
inherit (f) persistentStoragePath;
|
2022-01-28 11:58:14 +00:00
|
|
|
} // f.parentDirectory)
|
2022-01-15 16:12:17 +00:00
|
|
|
files);
|
2020-12-13 13:45:29 +00:00
|
|
|
in
|
2021-08-29 18:02:47 +00:00
|
|
|
{
|
2022-01-28 11:58:14 +00:00
|
|
|
"createPersistentStorageDirs" = {
|
|
|
|
deps = [ "users" "groups" ];
|
|
|
|
text = concatMapStrings mkDirWithPerms (directories ++ fileDirectories);
|
|
|
|
};
|
2021-08-29 18:02:47 +00:00
|
|
|
};
|
|
|
|
|
2022-01-28 11:58:14 +00:00
|
|
|
persistFileScript = { file, persistentStoragePath, ... }:
|
2021-08-29 18:02:47 +00:00
|
|
|
let
|
|
|
|
targetFile = escapeShellArg (concatPaths [ persistentStoragePath file ]);
|
|
|
|
mountPoint = escapeShellArg file;
|
|
|
|
in
|
|
|
|
{
|
|
|
|
"persist-${sanitizeName targetFile}" = {
|
2022-01-15 16:12:17 +00:00
|
|
|
deps = [ "createPersistentStorageDirs" ];
|
2021-08-29 18:02:47 +00:00
|
|
|
text = mkMountScript mountPoint targetFile;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
persistFileScripts =
|
2022-01-15 16:12:17 +00:00
|
|
|
foldl' recursiveUpdate { } (map persistFileScript files);
|
2020-06-04 21:42:16 +00:00
|
|
|
in
|
2021-08-29 18:02:47 +00:00
|
|
|
dirCreationScripts // persistFileScripts;
|
2020-06-04 21:42:16 +00:00
|
|
|
|
|
|
|
assertions =
|
|
|
|
let
|
2020-07-18 19:40:37 +00:00
|
|
|
markedNeededForBoot = cond: fs:
|
|
|
|
if config.fileSystems ? ${fs} then
|
2022-01-15 16:12:17 +00:00
|
|
|
config.fileSystems.${fs}.neededForBoot == cond
|
2020-07-18 19:40:37 +00:00
|
|
|
else
|
|
|
|
cond;
|
2022-01-15 16:12:17 +00:00
|
|
|
persistentStoragePaths = attrNames cfg;
|
2022-01-16 17:59:03 +00:00
|
|
|
usersPerPath = allPersistentStoragePaths.users;
|
|
|
|
homeDirOffenders =
|
|
|
|
filterAttrs
|
|
|
|
(n: v: (v.home != config.users.users.${n}.home));
|
2020-06-05 17:37:07 +00:00
|
|
|
in
|
|
|
|
[
|
2020-06-08 16:40:35 +00:00
|
|
|
{
|
|
|
|
# Assert that all persistent storage volumes we use are
|
|
|
|
# marked with neededForBoot.
|
|
|
|
assertion = all (markedNeededForBoot true) persistentStoragePaths;
|
|
|
|
message =
|
|
|
|
let
|
|
|
|
offenders = filter (markedNeededForBoot false) persistentStoragePaths;
|
|
|
|
in
|
|
|
|
''
|
|
|
|
environment.persistence:
|
|
|
|
All filesystems used for persistent storage must
|
|
|
|
have the flag neededForBoot set to true.
|
|
|
|
|
|
|
|
Please fix or remove the following paths:
|
2020-06-04 21:42:16 +00:00
|
|
|
${concatStringsSep "\n " offenders}
|
|
|
|
'';
|
|
|
|
}
|
2022-01-16 17:59:03 +00:00
|
|
|
{
|
|
|
|
assertion = all (users: (homeDirOffenders users) == { }) usersPerPath;
|
|
|
|
message =
|
|
|
|
let
|
|
|
|
offendersPerPath = filter (users: (homeDirOffenders users) != { }) usersPerPath;
|
|
|
|
offendersText =
|
|
|
|
concatMapStringsSep
|
|
|
|
"\n "
|
|
|
|
(offenders:
|
|
|
|
concatMapStringsSep
|
|
|
|
"\n "
|
|
|
|
(n: "${n}: ${offenders.${n}.home} != ${config.users.users.${n}.home}")
|
|
|
|
(attrNames offenders))
|
|
|
|
offendersPerPath;
|
|
|
|
in
|
|
|
|
''
|
|
|
|
environment.persistence:
|
|
|
|
Users and home doesn't match:
|
|
|
|
${offendersText}
|
|
|
|
|
|
|
|
You probably want to set each
|
|
|
|
environment.persistence.<path>.users.<user>.home to
|
|
|
|
match the respective user's home directory as
|
|
|
|
defined by users.users.<user>.home.
|
|
|
|
'';
|
|
|
|
}
|
2022-01-16 17:56:15 +00:00
|
|
|
{
|
|
|
|
assertion = duplicates (catAttrs "file" files) == [ ];
|
|
|
|
message =
|
|
|
|
let
|
|
|
|
offenders = duplicates (catAttrs "file" files);
|
|
|
|
in
|
|
|
|
''
|
|
|
|
environment.persistence:
|
|
|
|
The following files were specified two or more
|
|
|
|
times:
|
|
|
|
${concatStringsSep "\n " offenders}
|
|
|
|
'';
|
|
|
|
}
|
|
|
|
{
|
|
|
|
assertion = duplicates (catAttrs "directory" directories) == [ ];
|
|
|
|
message =
|
|
|
|
let
|
|
|
|
offenders = duplicates (catAttrs "directory" directories);
|
|
|
|
in
|
|
|
|
''
|
|
|
|
environment.persistence:
|
|
|
|
The following directories were specified two or more
|
|
|
|
times:
|
|
|
|
${concatStringsSep "\n " offenders}
|
|
|
|
'';
|
|
|
|
}
|
2020-06-04 21:42:16 +00:00
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|