2023-12-19 13:48:46 +00:00
|
|
|
|
{ pkgs, config, lib, utils, ... }:
|
2020-06-04 21:42:16 +00:00
|
|
|
|
|
|
|
|
|
let
|
2022-11-13 17:16:57 +00:00
|
|
|
|
inherit (lib)
|
|
|
|
|
attrNames
|
|
|
|
|
attrValues
|
|
|
|
|
zipAttrsWith
|
|
|
|
|
flatten
|
2024-02-24 13:13:56 +00:00
|
|
|
|
mkAfter
|
2022-11-13 17:16:57 +00:00
|
|
|
|
mkOption
|
|
|
|
|
mkDefault
|
2024-06-09 10:42:34 +00:00
|
|
|
|
mkIf
|
2024-08-20 09:24:24 +00:00
|
|
|
|
mkMerge
|
2022-11-13 17:16:57 +00:00
|
|
|
|
mapAttrsToList
|
|
|
|
|
types
|
|
|
|
|
foldl'
|
|
|
|
|
unique
|
2024-02-24 13:13:56 +00:00
|
|
|
|
concatMap
|
2022-11-13 17:16:57 +00:00
|
|
|
|
concatMapStrings
|
|
|
|
|
listToAttrs
|
|
|
|
|
escapeShellArg
|
|
|
|
|
escapeShellArgs
|
|
|
|
|
recursiveUpdate
|
|
|
|
|
all
|
|
|
|
|
filter
|
|
|
|
|
filterAttrs
|
|
|
|
|
concatStringsSep
|
|
|
|
|
concatMapStringsSep
|
|
|
|
|
catAttrs
|
|
|
|
|
optional
|
2024-08-20 08:40:19 +00:00
|
|
|
|
optionalString
|
2022-11-13 17:16:57 +00:00
|
|
|
|
literalExpression
|
2022-11-13 17:18:15 +00:00
|
|
|
|
elem
|
|
|
|
|
mapAttrs
|
2024-08-20 08:40:19 +00:00
|
|
|
|
intersectLists
|
2024-08-20 09:24:24 +00:00
|
|
|
|
any
|
|
|
|
|
id
|
2022-11-13 17:16:57 +00:00
|
|
|
|
;
|
2022-01-15 16:12:17 +00:00
|
|
|
|
|
2023-12-19 13:48:46 +00:00
|
|
|
|
inherit (utils)
|
|
|
|
|
escapeSystemdPath
|
2024-02-24 13:13:56 +00:00
|
|
|
|
fsNeededForBoot
|
2023-12-19 13:48:46 +00:00
|
|
|
|
;
|
|
|
|
|
|
2022-11-13 17:16:57 +00:00
|
|
|
|
inherit (pkgs.callPackage ./lib.nix { })
|
|
|
|
|
concatPaths
|
|
|
|
|
duplicates
|
2022-11-13 17:18:15 +00:00
|
|
|
|
parentsOf
|
2022-11-13 17:16:57 +00:00
|
|
|
|
;
|
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;
|
2024-06-09 10:42:34 +00:00
|
|
|
|
allPersistentStoragePaths = zipAttrsWith (_name: flatten) (filter (v: v.enable) (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
|
|
|
|
|
2024-06-23 14:28:37 +00:00
|
|
|
|
defaultPerms = {
|
|
|
|
|
mode = "0755";
|
|
|
|
|
user = "root";
|
|
|
|
|
group = "root";
|
|
|
|
|
};
|
|
|
|
|
|
2022-02-11 01:12:23 +00:00
|
|
|
|
# Create fileSystems bind mount entry.
|
2023-06-24 15:37:01 +00:00
|
|
|
|
mkBindMountNameValuePair = { dirPath, persistentStoragePath, hideMount, ... }: {
|
2022-11-13 17:17:56 +00:00
|
|
|
|
name = concatPaths [ "/" dirPath ];
|
2022-02-11 01:12:23 +00:00
|
|
|
|
value = {
|
2022-11-13 17:17:56 +00:00
|
|
|
|
device = concatPaths [ persistentStoragePath dirPath ];
|
2022-02-11 01:12:23 +00:00
|
|
|
|
noCheck = true;
|
nixos: Prevent redundant fstrim
The fstrim service, when enabled, by default trims all mounts listed in
fstab (as long as the underlying device supports discard), including
bind mounts. With impermanence, this can lead to a significant number of
redundant trim operations, since the persistent volume is trimmed once
for itself and then again once per persistent directory each. On my
system, this leads to fstrim.service taking about 10 minutes, during
which the system usability is somewhat degraded. In addition, it may be
desired to disable trim for a certain filesystem entirely, for example
when using btrfs with discard=async.
Mark our bind mounts with the X-fstrim.notrim option, which is ignored
by mount, but instructs fstrim to skip the mount point in question. The
persistent filesystem is still trimmed properly, since it has to be
listed in fstab itself as well.
2023-09-13 06:31:26 +00:00
|
|
|
|
options = [ "bind" "X-fstrim.notrim" ]
|
2023-06-24 15:37:01 +00:00
|
|
|
|
++ optional hideMount "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
|
|
|
|
|
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
|
|
|
|
|
commonOpts = {
|
|
|
|
|
options = {
|
|
|
|
|
persistentStoragePath = mkOption {
|
|
|
|
|
type = path;
|
2024-10-22 19:25:45 +00:00
|
|
|
|
default = config.persistentStoragePath;
|
2023-10-13 17:59:13 +00:00
|
|
|
|
defaultText = "environment.persistence.‹name›.persistentStoragePath";
|
2022-01-15 16:12:17 +00:00
|
|
|
|
description = ''
|
|
|
|
|
The path to persistent storage where the real
|
2023-06-24 15:37:01 +00:00
|
|
|
|
file or directory should be stored.
|
2022-01-15 16:12:17 +00:00
|
|
|
|
'';
|
|
|
|
|
};
|
2022-11-13 17:17:56 +00:00
|
|
|
|
home = mkOption {
|
|
|
|
|
type = nullOr path;
|
|
|
|
|
default = null;
|
|
|
|
|
internal = true;
|
|
|
|
|
description = ''
|
|
|
|
|
The path to the home directory the file is
|
|
|
|
|
placed within.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-06-24 15:37:01 +00:00
|
|
|
|
enableDebugging = mkOption {
|
|
|
|
|
type = bool;
|
2024-10-22 19:25:45 +00:00
|
|
|
|
default = config.enableDebugging;
|
2023-10-13 17:59:13 +00:00
|
|
|
|
defaultText = "environment.persistence.‹name›.enableDebugging";
|
2023-06-24 15:37:01 +00:00
|
|
|
|
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
|
|
|
|
};
|
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.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-06-24 15:37:01 +00:00
|
|
|
|
parentDirectory =
|
|
|
|
|
commonOpts.options //
|
|
|
|
|
mapAttrs
|
|
|
|
|
(_: x:
|
|
|
|
|
if x._type or null == "option" then
|
|
|
|
|
x // { internal = true; }
|
|
|
|
|
else
|
|
|
|
|
x)
|
|
|
|
|
dirOpts.options;
|
2022-11-13 17:17:56 +00:00
|
|
|
|
filePath = mkOption {
|
|
|
|
|
type = path;
|
|
|
|
|
internal = true;
|
|
|
|
|
};
|
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.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-06-24 15:37:01 +00:00
|
|
|
|
hideMount = mkOption {
|
|
|
|
|
type = bool;
|
2024-10-22 19:25:45 +00:00
|
|
|
|
default = config.hideMounts;
|
2023-10-13 17:59:13 +00:00
|
|
|
|
defaultText = "environment.persistence.‹name›.hideMounts";
|
2023-06-24 15:37:01 +00:00
|
|
|
|
example = true;
|
|
|
|
|
description = ''
|
|
|
|
|
Whether to hide bind mounts from showing up as
|
|
|
|
|
mounted drives.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2022-11-13 17:18:15 +00:00
|
|
|
|
# Save the default permissions at the level the
|
|
|
|
|
# directory resides. This used when creating its
|
|
|
|
|
# parent directories, giving them reasonable
|
|
|
|
|
# default permissions unaffected by the
|
|
|
|
|
# directory's own.
|
|
|
|
|
defaultPerms = mapAttrs (_: x: x // { internal = true; }) dirPermsOpts;
|
2022-11-13 17:17:56 +00:00
|
|
|
|
dirPath = mkOption {
|
|
|
|
|
type = path;
|
|
|
|
|
internal = true;
|
|
|
|
|
};
|
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
|
2022-11-13 17:17:56 +00:00
|
|
|
|
({ config, ... }: {
|
2023-06-24 15:37:01 +00:00
|
|
|
|
parentDirectory = mkDefault (defaultPerms // rec {
|
|
|
|
|
directory = dirOf config.file;
|
|
|
|
|
dirPath = directory;
|
|
|
|
|
inherit (config) persistentStoragePath;
|
|
|
|
|
inherit defaultPerms;
|
|
|
|
|
});
|
2022-11-13 17:17:56 +00:00
|
|
|
|
filePath = mkDefault config.file;
|
|
|
|
|
})
|
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
|
2022-11-13 17:17:56 +00:00
|
|
|
|
({ config, ... }: {
|
2022-11-13 17:18:15 +00:00
|
|
|
|
defaultPerms = mkDefault defaultPerms;
|
2022-11-13 17:17:56 +00:00
|
|
|
|
dirPath = mkDefault config.directory;
|
|
|
|
|
})
|
2022-11-13 17:16:18 +00:00
|
|
|
|
] ++ (mapAttrsToList (n: v: { ${n} = mkDefault v; }) defaultPerms));
|
2022-01-15 16:12:17 +00:00
|
|
|
|
in
|
|
|
|
|
{
|
|
|
|
|
options =
|
|
|
|
|
{
|
2023-05-14 20:59:19 +00:00
|
|
|
|
enable = mkOption {
|
|
|
|
|
type = bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description = "Whether to enable this persistent storage location.";
|
|
|
|
|
};
|
2023-06-24 15:37:01 +00:00
|
|
|
|
|
|
|
|
|
persistentStoragePath = mkOption {
|
|
|
|
|
type = path;
|
|
|
|
|
default = name;
|
2023-10-13 17:59:13 +00:00
|
|
|
|
defaultText = "‹name›";
|
2023-06-24 15:37:01 +00:00
|
|
|
|
description = ''
|
|
|
|
|
The path to persistent storage where the real
|
|
|
|
|
files and directories should be stored.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
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;
|
|
|
|
|
};
|
2022-11-13 17:17:56 +00:00
|
|
|
|
fileConfig =
|
|
|
|
|
{ config, ... }:
|
|
|
|
|
{
|
2023-06-24 15:37:01 +00:00
|
|
|
|
parentDirectory = rec {
|
|
|
|
|
directory = dirOf config.file;
|
|
|
|
|
dirPath = concatPaths [ config.home directory ];
|
|
|
|
|
inherit (config) persistentStoragePath home;
|
|
|
|
|
defaultPerms = userDefaultPerms;
|
|
|
|
|
};
|
|
|
|
|
filePath = concatPaths [ config.home config.file ];
|
2022-11-13 17:17:56 +00:00
|
|
|
|
};
|
2022-01-28 11:58:14 +00:00
|
|
|
|
userFile = submodule [
|
|
|
|
|
commonOpts
|
2022-11-13 17:16:18 +00:00
|
|
|
|
fileOpts
|
2022-11-13 17:17:56 +00:00
|
|
|
|
{ inherit (config) home; }
|
2023-06-24 15:37:01 +00:00
|
|
|
|
{
|
|
|
|
|
parentDirectory = mkDefault userDefaultPerms;
|
|
|
|
|
}
|
2022-11-13 17:17:56 +00:00
|
|
|
|
fileConfig
|
2022-01-28 11:58:14 +00:00
|
|
|
|
];
|
2022-11-13 17:17:56 +00:00
|
|
|
|
dirConfig =
|
|
|
|
|
{ config, ... }:
|
|
|
|
|
{
|
2022-11-13 17:18:15 +00:00
|
|
|
|
defaultPerms = mkDefault userDefaultPerms;
|
2023-06-24 15:37:01 +00:00
|
|
|
|
dirPath = concatPaths [ config.home config.directory ];
|
2022-11-13 17:17:56 +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
|
2022-11-13 17:17:56 +00:00
|
|
|
|
{ inherit (config) home; }
|
|
|
|
|
dirConfig
|
2022-11-13 17:16:18 +00:00
|
|
|
|
] ++ (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.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2022-11-13 17:17:56 +00:00
|
|
|
|
|
2022-01-16 17:59:03 +00:00
|
|
|
|
files = mkOption {
|
2022-11-13 17:17:21 +00:00
|
|
|
|
type = listOf (coercedTo str (f: { file = f; }) userFile);
|
2022-01-16 17:59:03 +00:00
|
|
|
|
default = [ ];
|
|
|
|
|
example = [
|
|
|
|
|
".screenrc"
|
|
|
|
|
];
|
|
|
|
|
description = ''
|
|
|
|
|
Files that should be stored in
|
|
|
|
|
persistent storage.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
directories = mkOption {
|
2022-11-13 17:17:21 +00:00
|
|
|
|
type = listOf (coercedTo str (d: { directory = d; }) userDir);
|
2022-01-16 17:59:03 +00:00
|
|
|
|
default = [ ];
|
|
|
|
|
example = [
|
|
|
|
|
"Downloads"
|
|
|
|
|
"Music"
|
|
|
|
|
"Pictures"
|
|
|
|
|
"Documents"
|
|
|
|
|
"Videos"
|
|
|
|
|
];
|
|
|
|
|
description = ''
|
|
|
|
|
Directories to bind mount to
|
|
|
|
|
persistent storage.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
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-11-13 17:17:21 +00:00
|
|
|
|
type = listOf (coercedTo str (f: { file = f; }) 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.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
directories = mkOption {
|
2022-11-13 17:17:21 +00:00
|
|
|
|
type = listOf (coercedTo str (d: { directory = d; }) 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.
|
|
|
|
|
'';
|
|
|
|
|
};
|
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.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2024-08-20 09:24:24 +00:00
|
|
|
|
|
|
|
|
|
enableWarnings = mkOption {
|
|
|
|
|
type = bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description = ''
|
|
|
|
|
Enable non-critical warnings.
|
|
|
|
|
'';
|
|
|
|
|
};
|
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
|
|
|
|
};
|
|
|
|
|
|
2024-11-10 09:03:56 +00:00
|
|
|
|
config = mkIf (allPersistentStoragePaths != { })
|
|
|
|
|
(mkMerge [
|
|
|
|
|
{
|
|
|
|
|
systemd.services =
|
2021-08-29 18:02:47 +00:00
|
|
|
|
let
|
2024-11-10 09:03:56 +00:00
|
|
|
|
mkPersistFileService = { filePath, persistentStoragePath, enableDebugging, ... }:
|
|
|
|
|
let
|
|
|
|
|
targetFile = escapeShellArg (concatPaths [ persistentStoragePath filePath ]);
|
|
|
|
|
mountPoint = escapeShellArg filePath;
|
|
|
|
|
in
|
|
|
|
|
{
|
|
|
|
|
"persist-${escapeSystemdPath targetFile}" = {
|
|
|
|
|
description = "Bind mount or link ${targetFile} to ${mountPoint}";
|
|
|
|
|
wantedBy = [ "local-fs.target" ];
|
|
|
|
|
before = [ "local-fs.target" ];
|
|
|
|
|
path = [ pkgs.util-linux ];
|
|
|
|
|
unitConfig.DefaultDependencies = false;
|
|
|
|
|
serviceConfig = {
|
|
|
|
|
Type = "oneshot";
|
|
|
|
|
RemainAfterExit = true;
|
|
|
|
|
ExecStart = "${mountFile} ${mountPoint} ${targetFile} ${escapeShellArg enableDebugging}";
|
|
|
|
|
ExecStop = pkgs.writeShellScript "unbindOrUnlink-${escapeSystemdPath targetFile}" ''
|
|
|
|
|
set -eu
|
|
|
|
|
if [[ -L ${mountPoint} ]]; then
|
|
|
|
|
rm ${mountPoint}
|
|
|
|
|
else
|
|
|
|
|
umount ${mountPoint}
|
|
|
|
|
rm ${mountPoint}
|
|
|
|
|
fi
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
};
|
2021-08-29 18:02:47 +00:00
|
|
|
|
};
|
2022-02-16 12:40:17 +00:00
|
|
|
|
in
|
2024-11-10 09:03:56 +00:00
|
|
|
|
foldl' recursiveUpdate { } (map mkPersistFileService files);
|
|
|
|
|
|
|
|
|
|
fileSystems = mkIf (directories != [ ]) bindMounts;
|
|
|
|
|
# So the mounts still make it into a VM built from `system.build.vm`
|
|
|
|
|
virtualisation.fileSystems = mkIf (directories != [ ]) bindMounts;
|
2020-11-12 09:55:46 +00:00
|
|
|
|
|
2024-11-10 09:03:56 +00:00
|
|
|
|
system.activationScripts =
|
2020-12-13 13:45:29 +00:00
|
|
|
|
let
|
2024-11-10 09:03:56 +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" { buildInputs = [ pkgs.bash ]; } ''
|
|
|
|
|
cp ${./create-directories.bash} $out
|
|
|
|
|
patchShebangs $out
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
mkDirWithPerms =
|
|
|
|
|
{ dirPath
|
|
|
|
|
, persistentStoragePath
|
|
|
|
|
, user
|
|
|
|
|
, group
|
|
|
|
|
, mode
|
|
|
|
|
, enableDebugging
|
|
|
|
|
, ...
|
|
|
|
|
}:
|
|
|
|
|
let
|
|
|
|
|
args = [
|
|
|
|
|
persistentStoragePath
|
|
|
|
|
dirPath
|
|
|
|
|
user
|
|
|
|
|
group
|
|
|
|
|
mode
|
|
|
|
|
enableDebugging
|
|
|
|
|
];
|
|
|
|
|
in
|
|
|
|
|
''
|
|
|
|
|
${createDirectories} ${escapeShellArgs args}
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
# Build an activation script which creates all persistent
|
|
|
|
|
# storage directories we want to bind mount.
|
|
|
|
|
dirCreationScript =
|
|
|
|
|
let
|
|
|
|
|
# The parent directories of files.
|
|
|
|
|
fileDirs = unique (catAttrs "parentDirectory" files);
|
|
|
|
|
|
|
|
|
|
# All the directories actually listed by the user and the
|
|
|
|
|
# parent directories of listed files.
|
|
|
|
|
explicitDirs = directories ++ fileDirs;
|
|
|
|
|
|
|
|
|
|
# Home directories have to be handled specially, since
|
|
|
|
|
# they're at the permissions boundary where they
|
|
|
|
|
# themselves should be owned by the user and have stricter
|
|
|
|
|
# permissions than regular directories, whereas its parent
|
|
|
|
|
# should be owned by root and have regular permissions.
|
|
|
|
|
#
|
|
|
|
|
# This simply collects all the home directories and sets
|
|
|
|
|
# the appropriate permissions and ownership.
|
|
|
|
|
homeDirs =
|
|
|
|
|
foldl'
|
|
|
|
|
(state: dir:
|
|
|
|
|
let
|
|
|
|
|
homeDir = {
|
|
|
|
|
directory = dir.home;
|
|
|
|
|
dirPath = dir.home;
|
|
|
|
|
home = null;
|
|
|
|
|
mode = "0700";
|
|
|
|
|
user = dir.user;
|
|
|
|
|
group = users.${dir.user}.group;
|
|
|
|
|
inherit defaultPerms;
|
|
|
|
|
inherit (dir) persistentStoragePath enableDebugging;
|
|
|
|
|
};
|
|
|
|
|
in
|
|
|
|
|
if dir.home != null then
|
|
|
|
|
if !(elem homeDir state) then
|
|
|
|
|
state ++ [ homeDir ]
|
|
|
|
|
else
|
|
|
|
|
state
|
|
|
|
|
else
|
|
|
|
|
state
|
|
|
|
|
)
|
|
|
|
|
[ ]
|
|
|
|
|
explicitDirs;
|
|
|
|
|
|
|
|
|
|
# Persistent storage directories. These need to be created
|
|
|
|
|
# unless they're at the root of a filesystem.
|
|
|
|
|
persistentStorageDirs =
|
|
|
|
|
foldl'
|
|
|
|
|
(state: dir:
|
|
|
|
|
let
|
|
|
|
|
persistentStorageDir = {
|
|
|
|
|
directory = dir.persistentStoragePath;
|
|
|
|
|
dirPath = dir.persistentStoragePath;
|
|
|
|
|
persistentStoragePath = "";
|
|
|
|
|
home = null;
|
|
|
|
|
inherit (dir) defaultPerms enableDebugging;
|
|
|
|
|
inherit (dir.defaultPerms) user group mode;
|
|
|
|
|
};
|
|
|
|
|
in
|
|
|
|
|
if dir.home == null && !(elem persistentStorageDir state) then
|
|
|
|
|
state ++ [ persistentStorageDir ]
|
|
|
|
|
else
|
|
|
|
|
state
|
|
|
|
|
)
|
|
|
|
|
[ ]
|
|
|
|
|
(explicitDirs ++ homeDirs);
|
|
|
|
|
|
|
|
|
|
# Generate entries for all parent directories of the
|
|
|
|
|
# argument directories, listed in the order they need to
|
|
|
|
|
# be created. The parent directories are assigned default
|
|
|
|
|
# permissions.
|
|
|
|
|
mkParentDirs = dirs:
|
2024-06-23 14:28:37 +00:00
|
|
|
|
let
|
2024-11-10 09:03:56 +00:00
|
|
|
|
# Create a new directory item from `dir`, the child
|
|
|
|
|
# directory item to inherit properties from and
|
|
|
|
|
# `path`, the parent directory path.
|
|
|
|
|
mkParent = dir: path: {
|
|
|
|
|
directory = path;
|
|
|
|
|
dirPath =
|
|
|
|
|
if dir.home != null then
|
|
|
|
|
concatPaths [ dir.home path ]
|
|
|
|
|
else
|
|
|
|
|
path;
|
|
|
|
|
inherit (dir) persistentStoragePath home enableDebugging;
|
2024-06-23 14:28:37 +00:00
|
|
|
|
inherit (dir.defaultPerms) user group mode;
|
|
|
|
|
};
|
2024-11-10 09:03:56 +00:00
|
|
|
|
# Create new directory items for all parent
|
|
|
|
|
# directories of a directory.
|
|
|
|
|
mkParents = dir:
|
|
|
|
|
map (mkParent dir) (parentsOf dir.directory);
|
2024-06-23 14:28:37 +00:00
|
|
|
|
in
|
2024-11-10 09:03:56 +00:00
|
|
|
|
unique (flatten (map mkParents dirs));
|
|
|
|
|
|
|
|
|
|
persistentStorageDirParents = mkParentDirs persistentStorageDirs;
|
|
|
|
|
|
|
|
|
|
# Parent directories of home folders. This is usually only
|
|
|
|
|
# /home, unless the user's home is in a non-standard
|
|
|
|
|
# location.
|
|
|
|
|
homeDirParents = mkParentDirs homeDirs;
|
|
|
|
|
|
|
|
|
|
# Parent directories of all explicitly listed directories.
|
|
|
|
|
parentDirs = mkParentDirs explicitDirs;
|
|
|
|
|
|
|
|
|
|
# All directories in the order they should be created.
|
|
|
|
|
allDirs =
|
|
|
|
|
persistentStorageDirParents
|
|
|
|
|
++ persistentStorageDirs
|
|
|
|
|
++ homeDirParents
|
|
|
|
|
++ homeDirs
|
|
|
|
|
++ parentDirs
|
|
|
|
|
++ explicitDirs;
|
|
|
|
|
in
|
|
|
|
|
pkgs.writeShellScript "impermanence-run-create-directories" ''
|
|
|
|
|
_status=0
|
|
|
|
|
trap "_status=1" ERR
|
|
|
|
|
${concatMapStrings mkDirWithPerms allDirs}
|
|
|
|
|
exit $_status
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
mkPersistFile = { filePath, persistentStoragePath, enableDebugging, ... }:
|
2022-11-13 17:18:15 +00:00
|
|
|
|
let
|
2024-11-10 09:03:56 +00:00
|
|
|
|
mountPoint = filePath;
|
|
|
|
|
targetFile = concatPaths [ persistentStoragePath filePath ];
|
|
|
|
|
args = escapeShellArgs [
|
|
|
|
|
mountPoint
|
|
|
|
|
targetFile
|
|
|
|
|
enableDebugging
|
|
|
|
|
];
|
2022-11-13 17:18:15 +00:00
|
|
|
|
in
|
2024-11-10 09:03:56 +00:00
|
|
|
|
''
|
|
|
|
|
${mountFile} ${args}
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
persistFileScript =
|
|
|
|
|
pkgs.writeShellScript "impermanence-persist-files" ''
|
|
|
|
|
_status=0
|
|
|
|
|
trap "_status=1" ERR
|
|
|
|
|
${concatMapStrings mkPersistFile files}
|
|
|
|
|
exit $_status
|
|
|
|
|
'';
|
2020-12-13 13:45:29 +00:00
|
|
|
|
in
|
2024-11-10 09:03:56 +00:00
|
|
|
|
{
|
|
|
|
|
"createPersistentStorageDirs" = {
|
|
|
|
|
deps = [ "users" "groups" ];
|
|
|
|
|
text = "${dirCreationScript}";
|
|
|
|
|
};
|
|
|
|
|
"persist-files" = {
|
|
|
|
|
deps = [ "createPersistentStorageDirs" ];
|
|
|
|
|
text = "${persistFileScript}";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# Create the mountpoints of directories marked as needed for boot
|
|
|
|
|
# which are also persisted. For this to work, it has to run at
|
|
|
|
|
# early boot, before NixOS' filesystem mounting runs. Without
|
|
|
|
|
# this, initial boot fails when for example /var/lib/nixos is
|
|
|
|
|
# persisted but not created in persistent storage.
|
|
|
|
|
boot.initrd =
|
2021-08-29 18:02:47 +00:00
|
|
|
|
let
|
2024-11-10 09:03:56 +00:00
|
|
|
|
neededForBootFs = catAttrs "mountPoint" (filter fsNeededForBoot (attrValues config.fileSystems));
|
|
|
|
|
neededForBootDirs = filter (dir: elem dir.dirPath neededForBootFs) directories;
|
|
|
|
|
getDevice = fs: if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}";
|
|
|
|
|
mkMount = fs:
|
|
|
|
|
let
|
|
|
|
|
mountPoint = concatPaths [ "/persist-tmp-mnt" fs.mountPoint ];
|
|
|
|
|
device = getDevice fs;
|
|
|
|
|
options = filter (o: (builtins.match "(x-.*\.mount)" o) == null) fs.options;
|
|
|
|
|
optionsFlag = optionalString (options != [ ]) ("-o " + escapeShellArg (concatStringsSep "," options));
|
|
|
|
|
in
|
|
|
|
|
''
|
|
|
|
|
mkdir -p ${escapeShellArg mountPoint}
|
|
|
|
|
mount -t ${escapeShellArgs [ fs.fsType device mountPoint ]} ${optionsFlag}
|
|
|
|
|
'';
|
|
|
|
|
mkDir = { persistentStoragePath, dirPath, ... }: ''
|
|
|
|
|
mkdir -p ${escapeShellArg (concatPaths [ "/persist-tmp-mnt" persistentStoragePath dirPath ])}
|
|
|
|
|
'';
|
|
|
|
|
mkUnmount = fs: ''
|
|
|
|
|
umount ${escapeShellArg (concatPaths [ "/persist-tmp-mnt" fs.mountPoint ])}
|
|
|
|
|
'';
|
|
|
|
|
fileSystems =
|
|
|
|
|
let
|
|
|
|
|
persistentStoragePaths = unique (catAttrs "persistentStoragePath" directories);
|
|
|
|
|
all = config.fileSystems // config.virtualisation.fileSystems;
|
|
|
|
|
matchFileSystems = fs: attrValues (filterAttrs (_: v: v.mountPoint or null == fs) all);
|
|
|
|
|
in
|
|
|
|
|
concatMap matchFileSystems persistentStoragePaths;
|
|
|
|
|
deviceUnits = unique
|
|
|
|
|
(map
|
|
|
|
|
(fs:
|
|
|
|
|
if fs.fsType == "zfs" then
|
|
|
|
|
"zfs-import.target"
|
|
|
|
|
else
|
|
|
|
|
"${(escapeSystemdPath (getDevice fs))}.device")
|
|
|
|
|
fileSystems);
|
|
|
|
|
createNeededForBootDirs = ''
|
|
|
|
|
${concatMapStrings mkMount fileSystems}
|
|
|
|
|
${concatMapStrings mkDir neededForBootDirs}
|
|
|
|
|
${concatMapStrings mkUnmount fileSystems}
|
|
|
|
|
'';
|
2021-08-29 18:02:47 +00:00
|
|
|
|
in
|
2024-11-10 09:03:56 +00:00
|
|
|
|
{
|
|
|
|
|
systemd.services = mkIf config.boot.initrd.systemd.enable {
|
|
|
|
|
create-needed-for-boot-dirs = {
|
|
|
|
|
wantedBy = [ "initrd-root-device.target" ];
|
|
|
|
|
requires = deviceUnits;
|
|
|
|
|
after = deviceUnits;
|
|
|
|
|
before = [ "sysroot.mount" ];
|
|
|
|
|
serviceConfig.Type = "oneshot";
|
|
|
|
|
unitConfig.DefaultDependencies = false;
|
|
|
|
|
script = createNeededForBootDirs;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable)
|
|
|
|
|
(mkAfter createNeededForBootDirs);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-10 09:04:30 +00:00
|
|
|
|
# Work around an issue with persisting /etc/machine-id where the
|
|
|
|
|
# systemd-machine-id-commit.service unit fails if
|
|
|
|
|
# /etc/machine-id is bind mounted from persistent storage. For
|
|
|
|
|
# more details, see
|
|
|
|
|
# https://github.com/nix-community/impermanence/issues/229
|
|
|
|
|
(mkIf (any (f: f == "/etc/machine-id") (catAttrs "filePath" files)) {
|
|
|
|
|
boot.initrd.systemd.suppressedUnits = [ "systemd-machine-id-commit.service" ];
|
|
|
|
|
systemd.suppressedSystemUnits = [ "systemd-machine-id-commit.service" ];
|
|
|
|
|
})
|
|
|
|
|
|
2024-11-10 09:03:56 +00:00
|
|
|
|
# Assertions and warnings
|
2022-02-16 12:42:10 +00:00
|
|
|
|
{
|
2024-11-10 09:03:56 +00:00
|
|
|
|
assertions =
|
2024-02-24 13:13:56 +00:00
|
|
|
|
let
|
2024-11-10 09:03:56 +00:00
|
|
|
|
markedNeededForBoot = cond: fs:
|
|
|
|
|
if config.fileSystems ? ${fs} then
|
|
|
|
|
config.fileSystems.${fs}.neededForBoot == cond
|
2024-09-28 10:43:57 +00:00
|
|
|
|
else
|
2024-11-10 09:03:56 +00:00
|
|
|
|
cond;
|
|
|
|
|
persistentStoragePaths = attrNames cfg;
|
|
|
|
|
usersPerPath = allPersistentStoragePaths.users;
|
|
|
|
|
homeDirOffenders =
|
|
|
|
|
filterAttrs
|
|
|
|
|
(n: v: (v.home != config.users.users.${n}.home));
|
|
|
|
|
in
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
# 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.
|
2020-06-08 16:40:35 +00:00
|
|
|
|
|
2024-11-10 09:03:56 +00:00
|
|
|
|
Please fix or remove the following paths:
|
|
|
|
|
${concatStringsSep "\n " offenders}
|
|
|
|
|
'';
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
assertion = all (users: (homeDirOffenders users) == { }) usersPerPath;
|
|
|
|
|
message =
|
|
|
|
|
let
|
|
|
|
|
offendersPerPath = filter (users: (homeDirOffenders users) != { }) usersPerPath;
|
|
|
|
|
offendersText =
|
2022-01-16 17:59:03 +00:00
|
|
|
|
concatMapStringsSep
|
|
|
|
|
"\n "
|
2024-11-10 09:03:56 +00:00
|
|
|
|
(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.
|
|
|
|
|
'';
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
assertion = duplicates (catAttrs "filePath" files) == [ ];
|
|
|
|
|
message =
|
|
|
|
|
let
|
|
|
|
|
offenders = duplicates (catAttrs "filePath" files);
|
|
|
|
|
in
|
|
|
|
|
''
|
|
|
|
|
environment.persistence:
|
|
|
|
|
The following files were specified two or more
|
|
|
|
|
times:
|
|
|
|
|
${concatStringsSep "\n " offenders}
|
|
|
|
|
'';
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
assertion = duplicates (catAttrs "dirPath" directories) == [ ];
|
|
|
|
|
message =
|
|
|
|
|
let
|
|
|
|
|
offenders = duplicates (catAttrs "dirPath" directories);
|
|
|
|
|
in
|
|
|
|
|
''
|
|
|
|
|
environment.persistence:
|
|
|
|
|
The following directories were specified two or more
|
|
|
|
|
times:
|
|
|
|
|
${concatStringsSep "\n " offenders}
|
|
|
|
|
'';
|
|
|
|
|
}
|
|
|
|
|
];
|
2024-08-20 08:40:19 +00:00
|
|
|
|
|
2024-11-10 09:03:56 +00:00
|
|
|
|
warnings =
|
2024-08-20 08:40:19 +00:00
|
|
|
|
let
|
2024-11-10 09:03:56 +00:00
|
|
|
|
usersWithoutUid = attrNames (filterAttrs (n: u: u.uid == null) config.users.users);
|
|
|
|
|
groupsWithoutGid = attrNames (filterAttrs (n: g: g.gid == null) config.users.groups);
|
|
|
|
|
varLibNixosPersistent =
|
|
|
|
|
let
|
|
|
|
|
varDirs = parentsOf "/var/lib/nixos" ++ [ "/var/lib/nixos" ];
|
|
|
|
|
persistedDirs = catAttrs "dirPath" directories;
|
|
|
|
|
mountedDirs = catAttrs "mountPoint" (attrValues config.fileSystems);
|
|
|
|
|
persistedVarDirs = intersectLists varDirs persistedDirs;
|
|
|
|
|
mountedVarDirs = intersectLists varDirs mountedDirs;
|
|
|
|
|
in
|
|
|
|
|
persistedVarDirs != [ ] || mountedVarDirs != [ ];
|
2024-08-20 08:40:19 +00:00
|
|
|
|
in
|
2024-11-10 09:03:56 +00:00
|
|
|
|
mkIf (any id allPersistentStoragePaths.enableWarnings)
|
|
|
|
|
(mkMerge [
|
|
|
|
|
(mkIf (!varLibNixosPersistent && (usersWithoutUid != [ ] || groupsWithoutGid != [ ])) [
|
|
|
|
|
''
|
|
|
|
|
environment.persistence:
|
|
|
|
|
Neither /var/lib/nixos nor any of its parents are
|
|
|
|
|
persisted. This means all users/groups without
|
|
|
|
|
specified uids/gids will have them reassigned on
|
|
|
|
|
reboot.
|
|
|
|
|
${optionalString (usersWithoutUid != [ ]) ''
|
|
|
|
|
The following users are missing a uid:
|
|
|
|
|
${concatStringsSep "\n " usersWithoutUid}
|
|
|
|
|
''}
|
|
|
|
|
${optionalString (groupsWithoutGid != [ ]) ''
|
|
|
|
|
The following groups are missing a gid:
|
|
|
|
|
${concatStringsSep "\n " groupsWithoutGid}
|
|
|
|
|
''}
|
|
|
|
|
''
|
|
|
|
|
])
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
]);
|
2020-06-04 21:42:16 +00:00
|
|
|
|
|
|
|
|
|
}
|