2020-06-04 21:42:16 +00:00
|
|
|
{ pkgs, config, lib, ... }:
|
|
|
|
|
|
|
|
let
|
2022-11-13 17:16:57 +00:00
|
|
|
inherit (lib)
|
|
|
|
attrNames
|
|
|
|
attrValues
|
|
|
|
zipAttrsWith
|
|
|
|
flatten
|
|
|
|
mkOption
|
|
|
|
mkDefault
|
|
|
|
mapAttrsToList
|
|
|
|
types
|
|
|
|
foldl'
|
|
|
|
unique
|
|
|
|
concatMapStrings
|
|
|
|
listToAttrs
|
|
|
|
escapeShellArg
|
|
|
|
escapeShellArgs
|
|
|
|
recursiveUpdate
|
|
|
|
all
|
|
|
|
filter
|
|
|
|
filterAttrs
|
|
|
|
concatStringsSep
|
|
|
|
concatMapStringsSep
|
|
|
|
isString
|
|
|
|
catAttrs
|
|
|
|
optional
|
|
|
|
literalExpression
|
|
|
|
;
|
2022-01-15 16:12:17 +00:00
|
|
|
|
2022-11-13 17:16:57 +00:00
|
|
|
inherit (pkgs.callPackage ./lib.nix { })
|
|
|
|
splitPath
|
|
|
|
dirListToPath
|
|
|
|
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;
|
2022-07-17 16:57:32 +00:00
|
|
|
mountFile = pkgs.runCommand "impermanence-mount-file" { buildInputs = [ pkgs.bash ]; } ''
|
2022-02-16 12:42:10 +00:00
|
|
|
cp ${./mount-file.bash} $out
|
|
|
|
patchShebangs $out
|
2021-08-29 18:02:47 +00:00
|
|
|
'';
|
2022-02-11 01:12:23 +00:00
|
|
|
|
|
|
|
# Create fileSystems bind mount entry.
|
|
|
|
mkBindMountNameValuePair = { directory, persistentStoragePath, ... }: {
|
|
|
|
name = concatPaths [ "/" directory ];
|
|
|
|
value = {
|
|
|
|
device = concatPaths [ persistentStoragePath directory ];
|
|
|
|
noCheck = true;
|
|
|
|
options = [ "bind" ]
|
|
|
|
++ optional cfg.${persistentStoragePath}.hideMounts "x-gvfs-hide";
|
2023-02-02 17:28:09 +00:00
|
|
|
depends = [ persistentStoragePath ];
|
2022-02-11 01:12:23 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
# Create all fileSystems bind mount entries for a specific
|
|
|
|
# persistent storage path.
|
|
|
|
bindMounts = listToAttrs (map mkBindMountNameValuePair directories);
|
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-11-13 17:16:57 +00:00
|
|
|
inherit (types)
|
|
|
|
attrsOf
|
|
|
|
bool
|
|
|
|
listOf
|
|
|
|
submodule
|
|
|
|
nullOr
|
|
|
|
path
|
|
|
|
either
|
|
|
|
str
|
|
|
|
coercedTo
|
|
|
|
;
|
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-11-13 17:16:18 +00:00
|
|
|
dirPermsOpts = {
|
2022-01-28 11:58:14 +00:00
|
|
|
user = mkOption {
|
|
|
|
type = str;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
example = "0700";
|
|
|
|
description = ''
|
|
|
|
If the directory doesn't exist in persistent
|
|
|
|
storage it will be created with the mode
|
|
|
|
specified by this option.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
2022-11-13 17:16:18 +00:00
|
|
|
fileOpts = {
|
2022-01-15 16:12:17 +00:00
|
|
|
options = {
|
|
|
|
file = mkOption {
|
|
|
|
type = str;
|
|
|
|
description = ''
|
|
|
|
The path to the file.
|
|
|
|
'';
|
|
|
|
};
|
2022-11-13 17:16:18 +00:00
|
|
|
parentDirectory = dirPermsOpts;
|
2022-01-15 16:12:17 +00:00
|
|
|
};
|
2020-06-04 21:42:16 +00:00
|
|
|
};
|
2022-11-13 17:16:18 +00:00
|
|
|
dirOpts = {
|
2022-01-15 16:12:17 +00:00
|
|
|
options = {
|
|
|
|
directory = mkOption {
|
|
|
|
type = str;
|
|
|
|
description = ''
|
|
|
|
The path to the directory.
|
|
|
|
'';
|
|
|
|
};
|
2022-11-13 17:16:18 +00:00
|
|
|
} // dirPermsOpts;
|
2022-01-15 16:12:17 +00:00
|
|
|
};
|
2022-01-28 11:58:14 +00:00
|
|
|
rootFile = submodule [
|
|
|
|
commonOpts
|
2022-11-13 17:16:18 +00:00
|
|
|
fileOpts
|
|
|
|
{ parentDirectory = mkDefault defaultPerms; }
|
2022-01-28 11:58:14 +00:00
|
|
|
];
|
2022-11-13 17:16:18 +00:00
|
|
|
rootDir = submodule ([
|
2022-01-28 11:58:14 +00:00
|
|
|
commonOpts
|
2022-11-13 17:16:18 +00:00
|
|
|
dirOpts
|
|
|
|
] ++ (mapAttrsToList (n: v: { ${n} = mkDefault v; }) 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
|
2022-11-13 17:16:18 +00:00
|
|
|
fileOpts
|
|
|
|
{ parentDirectory = mkDefault userDefaultPerms; }
|
2022-01-28 11:58:14 +00:00
|
|
|
];
|
2022-11-13 17:16:18 +00:00
|
|
|
userDir = submodule ([
|
2022-01-28 11:58:14 +00:00
|
|
|
commonOpts
|
2022-11-13 17:16:18 +00:00
|
|
|
dirOpts
|
|
|
|
] ++ (mapAttrsToList (n: v: { ${n} = mkDefault v; }) userDefaultPerms));
|
2022-01-28 11:58:14 +00:00
|
|
|
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"
|
2023-02-02 16:23:19 +00:00
|
|
|
"/var/lib/nixos"
|
2022-01-15 16:12:17 +00:00
|
|
|
"/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-02-16 12:40:17 +00:00
|
|
|
|
|
|
|
enableDebugging = mkOption {
|
|
|
|
type = bool;
|
|
|
|
default = false;
|
|
|
|
internal = true;
|
|
|
|
description = ''
|
|
|
|
Enable debug trace output when running
|
|
|
|
scripts. You only need to enable this if asked
|
|
|
|
to.
|
|
|
|
'';
|
|
|
|
};
|
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"
|
2023-02-02 16:23:19 +00:00
|
|
|
"/var/lib/nixos"
|
2022-02-13 22:25:51 +00:00
|
|
|
"/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
|
|
|
};
|
|
|
|
|
2022-02-11 01:12:23 +00:00
|
|
|
# Forward declare a dummy option for VM filesystems since the real one won't exist
|
|
|
|
# unless the VM module is actually imported.
|
|
|
|
virtualisation.fileSystems = mkOption { };
|
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;
|
2022-02-16 12:42:10 +00:00
|
|
|
enableDebugging = escapeShellArg cfg.${persistentStoragePath}.enableDebugging;
|
2021-08-29 18:02:47 +00:00
|
|
|
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;
|
2022-02-16 12:42:10 +00:00
|
|
|
ExecStart = "${mountFile} ${mountPoint} ${targetFile} ${enableDebugging}";
|
2021-08-29 18:02:47 +00:00
|
|
|
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
|
|
|
|
2022-02-11 01:12:23 +00:00
|
|
|
fileSystems = bindMounts;
|
|
|
|
# So the mounts still make it into a VM built from `system.build.vm`
|
|
|
|
virtualisation.fileSystems = bindMounts;
|
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.
|
2022-07-17 16:57:32 +00:00
|
|
|
createDirectories = pkgs.runCommand "impermanence-create-directories" { buildInputs = [ pkgs.bash ]; } ''
|
2020-12-13 13:15:58 +00:00
|
|
|
cp ${./create-directories.bash} $out
|
|
|
|
patchShebangs $out
|
|
|
|
'';
|
2020-06-04 21:42:16 +00:00
|
|
|
|
2022-02-16 12:40:17 +00:00
|
|
|
mkDirWithPerms = { directory, persistentStoragePath, user, group, mode }:
|
|
|
|
let
|
|
|
|
args = [
|
|
|
|
persistentStoragePath
|
|
|
|
directory
|
|
|
|
user
|
|
|
|
group
|
|
|
|
mode
|
|
|
|
cfg.${persistentStoragePath}.enableDebugging
|
|
|
|
];
|
|
|
|
in
|
|
|
|
''
|
|
|
|
${createDirectories} ${escapeShellArgs args}
|
|
|
|
'';
|
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-02-16 12:42:10 +00:00
|
|
|
dirCreationScript =
|
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
|
2022-02-16 12:42:10 +00:00
|
|
|
pkgs.writeShellScript "impermanence-run-create-directories" ''
|
|
|
|
_status=0
|
|
|
|
trap "_status=1" ERR
|
|
|
|
${concatMapStrings mkDirWithPerms (directories ++ fileDirectories)}
|
|
|
|
exit $_status
|
|
|
|
'';
|
2021-08-29 18:02:47 +00:00
|
|
|
|
2022-02-16 12:42:10 +00:00
|
|
|
mkPersistFile = { file, persistentStoragePath, ... }:
|
2021-08-29 18:02:47 +00:00
|
|
|
let
|
2022-02-16 12:42:10 +00:00
|
|
|
mountPoint = file;
|
|
|
|
targetFile = concatPaths [ persistentStoragePath file ];
|
|
|
|
args = escapeShellArgs [
|
|
|
|
mountPoint
|
|
|
|
targetFile
|
|
|
|
cfg.${persistentStoragePath}.enableDebugging
|
|
|
|
];
|
2021-08-29 18:02:47 +00:00
|
|
|
in
|
2022-02-16 12:42:10 +00:00
|
|
|
''
|
|
|
|
${mountFile} ${args}
|
|
|
|
'';
|
2021-08-29 18:02:47 +00:00
|
|
|
|
2022-02-16 12:42:10 +00:00
|
|
|
persistFileScript =
|
|
|
|
pkgs.writeShellScript "impermanence-persist-files" ''
|
|
|
|
_status=0
|
|
|
|
trap "_status=1" ERR
|
|
|
|
${concatMapStrings mkPersistFile files}
|
|
|
|
exit $_status
|
|
|
|
'';
|
2020-06-04 21:42:16 +00:00
|
|
|
in
|
2022-02-16 12:42:10 +00:00
|
|
|
{
|
|
|
|
"createPersistentStorageDirs" = {
|
|
|
|
deps = [ "users" "groups" ];
|
|
|
|
text = "${dirCreationScript}";
|
|
|
|
};
|
|
|
|
"persist-files" = {
|
|
|
|
deps = [ "createPersistentStorageDirs" ];
|
|
|
|
text = "${persistFileScript}";
|
|
|
|
};
|
|
|
|
};
|
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
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|