fs/impermanence: more precisely control unit dependencies/ordering
This commit is contained in:
parent
592d17b725
commit
2ba6116f10
|
@ -7,7 +7,10 @@ let
|
||||||
# see nixpkgs/nixos/modules/services/networking/dhcpcd.nix
|
# see nixpkgs/nixos/modules/services/networking/dhcpcd.nix
|
||||||
hasDHCP = config.networking.dhcpcd.enable &&
|
hasDHCP = config.networking.dhcpcd.enable &&
|
||||||
(config.networking.useDHCP || any (i: i.useDHCP == true) (attrValues config.networking.interfaces));
|
(config.networking.useDHCP || any (i: i.useDHCP == true) (attrValues config.networking.interfaces));
|
||||||
|
mkSymlink = target: {
|
||||||
|
symlink.target = target;
|
||||||
|
wantedBeforeBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
|
@ -103,14 +106,14 @@ in
|
||||||
];
|
];
|
||||||
|
|
||||||
# convenience
|
# convenience
|
||||||
sane.fs."/home/colin/knowledge".symlink.target = "/home/colin/private/knowledge";
|
sane.fs."/home/colin/knowledge" = mkSymlink "/home/colin/private/knowledge";
|
||||||
sane.fs."/home/colin/nixos".symlink.target = "/home/colin/dev/nixos";
|
sane.fs."/home/colin/nixos" = mkSymlink "/home/colin/dev/nixos";
|
||||||
sane.fs."/home/colin/Videos/servo".symlink.target = "/mnt/servo-media/Videos";
|
sane.fs."/home/colin/Videos/servo" = mkSymlink "/mnt/servo-media/Videos";
|
||||||
sane.fs."/home/colin/Videos/servo-incomplete".symlink.target = "/mnt/servo-media/incomplete";
|
sane.fs."/home/colin/Videos/servo-incomplete" = mkSymlink "/mnt/servo-media/incomplete";
|
||||||
sane.fs."/home/colin/Music/servo".symlink.target = "/mnt/servo-media/Music";
|
sane.fs."/home/colin/Music/servo" = mkSymlink "/mnt/servo-media/Music";
|
||||||
|
|
||||||
# used by password managers, e.g. unix `pass`
|
# used by password managers, e.g. unix `pass`
|
||||||
sane.fs."/home/colin/.password-store".symlink.target = "/home/colin/knowledge/secrets/accounts";
|
sane.fs."/home/colin/.password-store" = mkSymlink "/home/colin/knowledge/secrets/accounts";
|
||||||
|
|
||||||
sane.impermanence.dirs.sys.plaintext = mkIf cfg.guest.enable [
|
sane.impermanence.dirs.sys.plaintext = mkIf cfg.guest.enable [
|
||||||
# intentionally allow other users to write to the guest folder
|
# intentionally allow other users to write to the guest folder
|
||||||
|
|
|
@ -32,6 +32,21 @@ let
|
||||||
type = types.nullOr (mountEntryFor name);
|
type = types.nullOr (mountEntryFor name);
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
|
wantedBy = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
list of units or targets which, when activated, should trigger this fs entry to be created.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
wantedBeforeBy = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
list of units or targets which, when activated, should first start and wait for this fs entry to be created.
|
||||||
|
if this unit fails, it will not block the targets in this list.
|
||||||
|
'';
|
||||||
|
};
|
||||||
unit = mkOption {
|
unit = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = "name of the systemd unit which ensures this entry";
|
description = "name of the systemd unit which ensures this entry";
|
||||||
|
@ -56,10 +71,6 @@ let
|
||||||
(lib.mkIf (config.symlink != null)
|
(lib.mkIf (config.symlink != null)
|
||||||
(sane-lib.filterNonNull config.symlink.acl))
|
(sane-lib.filterNonNull config.symlink.acl))
|
||||||
];
|
];
|
||||||
generated.reverseDepends = lib.mkMerge [
|
|
||||||
(lib.mkIf (config.dir != null) config.dir.reverseDepends)
|
|
||||||
(lib.mkIf (config.symlink != null) config.symlink.reverseDepends)
|
|
||||||
];
|
|
||||||
|
|
||||||
# actually generate the item
|
# actually generate the item
|
||||||
generated.script = lib.mkMerge [
|
generated.script = lib.mkMerge [
|
||||||
|
@ -90,11 +101,6 @@ let
|
||||||
type = sane-types.aclOverride;
|
type = sane-types.aclOverride;
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
reverseDepends = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
description = "list of systemd units which should be made to depend on this item (controls `wantedBy` and `before`)";
|
|
||||||
default = [];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,18 +115,11 @@ let
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = "fs path to link to";
|
description = "fs path to link to";
|
||||||
};
|
};
|
||||||
reverseDepends = propagatedGenerateMod.options.reverseDepends // {
|
|
||||||
# symlinks are terminal, so by default create them during startup
|
|
||||||
default = [ "multi-user.target" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
generatedEntry = types.submodule {
|
generatedEntry = types.submodule {
|
||||||
options = {
|
options = {
|
||||||
# we use a stricter acl type here, so don't inherit that.
|
|
||||||
inherit (propagatedGenerateMod.options) reverseDepends;
|
|
||||||
|
|
||||||
acl = mkOption {
|
acl = mkOption {
|
||||||
type = sane-types.acl;
|
type = sane-types.acl;
|
||||||
};
|
};
|
||||||
|
@ -153,9 +152,11 @@ let
|
||||||
description = "fs path to bind-mount from";
|
description = "fs path to bind-mount from";
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
extraOptions = mkOption {
|
depends = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
description = "extra fstab options for this mount";
|
description = ''
|
||||||
|
list of systemd units needed to be run before this entry can be mounted
|
||||||
|
'';
|
||||||
default = [];
|
default = [];
|
||||||
};
|
};
|
||||||
unit = mkOption {
|
unit = mkOption {
|
||||||
|
@ -166,7 +167,8 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
mkGeneratedConfig = path: gen-opt: let
|
mkGeneratedConfig = path: opt: let
|
||||||
|
gen-opt = opt.generated;
|
||||||
wrapper = generateWrapperScript path gen-opt;
|
wrapper = generateWrapperScript path gen-opt;
|
||||||
in {
|
in {
|
||||||
systemd.services."${serviceNameFor path}" = {
|
systemd.services."${serviceNameFor path}" = {
|
||||||
|
@ -182,8 +184,8 @@ let
|
||||||
# see: <https://www.freedesktop.org/software/systemd/man/systemd.special.html>
|
# see: <https://www.freedesktop.org/software/systemd/man/systemd.special.html>
|
||||||
unitConfig.DefaultDependencies = "no";
|
unitConfig.DefaultDependencies = "no";
|
||||||
|
|
||||||
wantedBy = gen-opt.reverseDepends;
|
before = opt.wantedBeforeBy;
|
||||||
before = gen-opt.reverseDepends;
|
wantedBy = opt.wantedBy ++ opt.wantedBeforeBy;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -198,6 +200,10 @@ let
|
||||||
device = ifBind opt.mount.bind;
|
device = ifBind opt.mount.bind;
|
||||||
options = (if isBind then ["bind"] else [])
|
options = (if isBind then ["bind"] else [])
|
||||||
++ [
|
++ [
|
||||||
|
# disable defaults: don't require this to be mount as part of local-fs.target
|
||||||
|
# we'll handle that stuff precisely.
|
||||||
|
"noauto"
|
||||||
|
"nofail"
|
||||||
# x-systemd options documented here:
|
# x-systemd options documented here:
|
||||||
# - <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
# - <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
||||||
# we can't mount this until after the underlying path is prepared.
|
# we can't mount this until after the underlying path is prepared.
|
||||||
|
@ -206,14 +212,16 @@ let
|
||||||
# the mount depends on its target directory being prepared
|
# the mount depends on its target directory being prepared
|
||||||
"x-systemd.requires=${opt.generated.unit}"
|
"x-systemd.requires=${opt.generated.unit}"
|
||||||
]
|
]
|
||||||
++ opt.mount.extraOptions;
|
++ (builtins.map (unit: "x-systemd.requires=${unit}") opt.mount.depends)
|
||||||
|
++ (builtins.map (unit: "x-systemd.before=${unit}") opt.wantedBeforeBy)
|
||||||
|
++ (builtins.map (unit: "x-systemd.wanted-by=${unit}") (opt.wantedBy ++ opt.wantedBeforeBy));
|
||||||
noCheck = ifBind true;
|
noCheck = ifBind true;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
mkFsConfig = path: opt: mergeTopLevel [
|
mkFsConfig = path: opt: mergeTopLevel [
|
||||||
(mkGeneratedConfig path opt.generated)
|
(mkGeneratedConfig path opt)
|
||||||
(lib.mkIf (opt.mount != null) (mkMountConfig path opt))
|
(lib.mkIf (opt.mount != null) (mkMountConfig path opt))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -34,12 +34,19 @@ let
|
||||||
/mnt/crypt/private/var/private/www/root.
|
/mnt/crypt/private/var/private/www/root.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
extraOptions = mkOption {
|
defaultOrdering.wantedBeforeBy = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [ "local-fs.target" ];
|
||||||
description = ''
|
description = ''
|
||||||
extra fstab options to include in all mounts downstream of this store.
|
list of units or targets which would prefer that everything in this store
|
||||||
e.g. ["noauto" "x-systemd.wanted-by=<blah>"] to automount but only after the store is explicitly unlocked.
|
be initialized before they run, but failing to do so should not error the items in this list.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
defaultOrdering.wantedBy = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
list of units or targets which, upon activation, should activate all units in this store.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -168,7 +175,7 @@ in
|
||||||
# inherit perms & make sure we don't mount until after the mount point is setup correctly.
|
# inherit perms & make sure we don't mount until after the mount point is setup correctly.
|
||||||
dir.acl = dir-acl;
|
dir.acl = dir-acl;
|
||||||
mount.bind = backing-path;
|
mount.bind = backing-path;
|
||||||
mount.extraOptions = store.extraOptions;
|
inherit (store.defaultOrdering) wantedBy wantedBeforeBy;
|
||||||
};
|
};
|
||||||
sane.fs."${backing-path}" = {
|
sane.fs."${backing-path}" = {
|
||||||
# ensure the backing path has same perms as the mount point.
|
# ensure the backing path has same perms as the mount point.
|
||||||
|
|
|
@ -34,7 +34,14 @@ lib.mkIf config.sane.impermanence.enable
|
||||||
noCheck = true;
|
noCheck = true;
|
||||||
};
|
};
|
||||||
# let sane.fs know about our fileSystem and automatically add the appropriate dependencies
|
# let sane.fs know about our fileSystem and automatically add the appropriate dependencies
|
||||||
sane.fs."${store.device}".mount = {};
|
sane.fs."${store.device}".mount = {
|
||||||
|
# technically the dependency on the keyfile is extraneous because that *happens* to
|
||||||
|
# be needed to init the store.
|
||||||
|
depends = let
|
||||||
|
cryptfile = config.sane.fs."${store.underlying.path}/gocryptfs.conf";
|
||||||
|
keyfile = config.sane.fs."${store.underlying.key}";
|
||||||
|
in [ keyfile.unit cryptfile.unit ];
|
||||||
|
};
|
||||||
|
|
||||||
# let sane.fs know how to initialize the gocryptfs store,
|
# let sane.fs know how to initialize the gocryptfs store,
|
||||||
# and that it MUST do so
|
# and that it MUST do so
|
||||||
|
@ -50,8 +57,6 @@ lib.mkIf config.sane.impermanence.enable
|
||||||
script.scriptArgs = [ store.underlying.path store.underlying.key ];
|
script.scriptArgs = [ store.underlying.path store.underlying.key ];
|
||||||
# we need the key in order to initialize the store
|
# we need the key in order to initialize the store
|
||||||
depends = [ config.sane.fs."${store.underlying.key}".unit ];
|
depends = [ config.sane.fs."${store.underlying.key}".unit ];
|
||||||
# the store must be initialized before we can mount it
|
|
||||||
reverseDepends = [ config.sane.fs."${store.device}".unit ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# let sane.fs know how to generate the key for gocryptfs
|
# let sane.fs know how to generate the key for gocryptfs
|
||||||
|
|
|
@ -12,14 +12,14 @@ lib.mkIf config.sane.impermanence.enable
|
||||||
# /home/colin/foo/bar when stored in `private` is visible at
|
# /home/colin/foo/bar when stored in `private` is visible at
|
||||||
# /home/colin/private/foo/bar
|
# /home/colin/private/foo/bar
|
||||||
prefix = "/home/colin";
|
prefix = "/home/colin";
|
||||||
# fstab options inherited by all members of the store
|
defaultOrdering = let
|
||||||
extraOptions = let
|
|
||||||
private-unit = config.sane.fs."/home/colin/private".unit;
|
private-unit = config.sane.fs."/home/colin/private".unit;
|
||||||
in [
|
in {
|
||||||
"noauto"
|
# auto create only after ~/private is mounted
|
||||||
# auto mount when ~/private is mounted
|
wantedBy = [ private-unit ];
|
||||||
"x-systemd.wanted-by=${private-unit}"
|
# we can't create things in private before local-fs.target
|
||||||
];
|
wantedBeforeBy = [ ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fileSystems."/home/colin/private" = {
|
fileSystems."/home/colin/private" = {
|
||||||
|
@ -27,6 +27,7 @@ lib.mkIf config.sane.impermanence.enable
|
||||||
fsType = "fuse.gocryptfs";
|
fsType = "fuse.gocryptfs";
|
||||||
options = [
|
options = [
|
||||||
"noauto" # don't try to mount, until the user logs in!
|
"noauto" # don't try to mount, until the user logs in!
|
||||||
|
"nofail"
|
||||||
"allow_other" # root ends up being the user that mounts this, so need to make it visible to `colin`.
|
"allow_other" # root ends up being the user that mounts this, so need to make it visible to `colin`.
|
||||||
"nodev"
|
"nodev"
|
||||||
"nosuid"
|
"nosuid"
|
||||||
|
@ -36,20 +37,9 @@ lib.mkIf config.sane.impermanence.enable
|
||||||
noCheck = true;
|
noCheck = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.fs."/home/colin/private" = {
|
# let sane.fs know about the endpoints
|
||||||
# let sane.fs know that this corresponds to a fileSystems entry
|
sane.fs."/home/colin/private".mount = {};
|
||||||
mount = {};
|
sane.fs."/nix/persist/home/colin/private".dir = {};
|
||||||
dir.reverseDepends = [
|
|
||||||
# ensure the directory is created during boot, and before user logs in.
|
|
||||||
"multi-user.target"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
sane.fs."/nix/persist/home/colin/private" = {
|
|
||||||
dir.reverseDepends = [
|
|
||||||
# ensure the directory is created during boot, and before user logs in.
|
|
||||||
"multi-user.target"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
|
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
|
||||||
system.fsPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
|
system.fsPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
|
||||||
|
|
Loading…
Reference in New Issue
Block a user