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
|
||||
hasDHCP = config.networking.dhcpcd.enable &&
|
||||
(config.networking.useDHCP || any (i: i.useDHCP == true) (attrValues config.networking.interfaces));
|
||||
|
||||
mkSymlink = target: {
|
||||
symlink.target = target;
|
||||
wantedBeforeBy = [ "multi-user.target" ];
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
|
@ -103,14 +106,14 @@ in
|
|||
];
|
||||
|
||||
# convenience
|
||||
sane.fs."/home/colin/knowledge".symlink.target = "/home/colin/private/knowledge";
|
||||
sane.fs."/home/colin/nixos".symlink.target = "/home/colin/dev/nixos";
|
||||
sane.fs."/home/colin/Videos/servo".symlink.target = "/mnt/servo-media/Videos";
|
||||
sane.fs."/home/colin/Videos/servo-incomplete".symlink.target = "/mnt/servo-media/incomplete";
|
||||
sane.fs."/home/colin/Music/servo".symlink.target = "/mnt/servo-media/Music";
|
||||
sane.fs."/home/colin/knowledge" = mkSymlink "/home/colin/private/knowledge";
|
||||
sane.fs."/home/colin/nixos" = mkSymlink "/home/colin/dev/nixos";
|
||||
sane.fs."/home/colin/Videos/servo" = mkSymlink "/mnt/servo-media/Videos";
|
||||
sane.fs."/home/colin/Videos/servo-incomplete" = mkSymlink "/mnt/servo-media/incomplete";
|
||||
sane.fs."/home/colin/Music/servo" = mkSymlink "/mnt/servo-media/Music";
|
||||
|
||||
# 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 [
|
||||
# intentionally allow other users to write to the guest folder
|
||||
|
|
|
@ -32,6 +32,21 @@ let
|
|||
type = types.nullOr (mountEntryFor name);
|
||||
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 {
|
||||
type = types.str;
|
||||
description = "name of the systemd unit which ensures this entry";
|
||||
|
@ -56,10 +71,6 @@ let
|
|||
(lib.mkIf (config.symlink != null)
|
||||
(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
|
||||
generated.script = lib.mkMerge [
|
||||
|
@ -90,11 +101,6 @@ let
|
|||
type = sane-types.aclOverride;
|
||||
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;
|
||||
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 {
|
||||
options = {
|
||||
# we use a stricter acl type here, so don't inherit that.
|
||||
inherit (propagatedGenerateMod.options) reverseDepends;
|
||||
|
||||
acl = mkOption {
|
||||
type = sane-types.acl;
|
||||
};
|
||||
|
@ -153,9 +152,11 @@ let
|
|||
description = "fs path to bind-mount from";
|
||||
default = null;
|
||||
};
|
||||
extraOptions = mkOption {
|
||||
depends = mkOption {
|
||||
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 = [];
|
||||
};
|
||||
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;
|
||||
in {
|
||||
systemd.services."${serviceNameFor path}" = {
|
||||
|
@ -182,8 +184,8 @@ let
|
|||
# see: <https://www.freedesktop.org/software/systemd/man/systemd.special.html>
|
||||
unitConfig.DefaultDependencies = "no";
|
||||
|
||||
wantedBy = gen-opt.reverseDepends;
|
||||
before = gen-opt.reverseDepends;
|
||||
before = opt.wantedBeforeBy;
|
||||
wantedBy = opt.wantedBy ++ opt.wantedBeforeBy;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -198,6 +200,10 @@ let
|
|||
device = ifBind opt.mount.bind;
|
||||
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:
|
||||
# - <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
||||
# 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
|
||||
"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;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
mkFsConfig = path: opt: mergeTopLevel [
|
||||
(mkGeneratedConfig path opt.generated)
|
||||
(mkGeneratedConfig path opt)
|
||||
(lib.mkIf (opt.mount != null) (mkMountConfig path opt))
|
||||
];
|
||||
|
||||
|
|
|
@ -34,12 +34,19 @@ let
|
|||
/mnt/crypt/private/var/private/www/root.
|
||||
'';
|
||||
};
|
||||
extraOptions = mkOption {
|
||||
defaultOrdering.wantedBeforeBy = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
default = [ "local-fs.target" ];
|
||||
description = ''
|
||||
extra fstab options to include in all mounts downstream of this store.
|
||||
e.g. ["noauto" "x-systemd.wanted-by=<blah>"] to automount but only after the store is explicitly unlocked.
|
||||
list of units or targets which would prefer that everything in this store
|
||||
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.
|
||||
dir.acl = dir-acl;
|
||||
mount.bind = backing-path;
|
||||
mount.extraOptions = store.extraOptions;
|
||||
inherit (store.defaultOrdering) wantedBy wantedBeforeBy;
|
||||
};
|
||||
sane.fs."${backing-path}" = {
|
||||
# ensure the backing path has same perms as the mount point.
|
||||
|
|
|
@ -34,7 +34,14 @@ lib.mkIf config.sane.impermanence.enable
|
|||
noCheck = true;
|
||||
};
|
||||
# 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,
|
||||
# and that it MUST do so
|
||||
|
@ -50,8 +57,6 @@ lib.mkIf config.sane.impermanence.enable
|
|||
script.scriptArgs = [ store.underlying.path store.underlying.key ];
|
||||
# we need the key in order to initialize the store
|
||||
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
|
||||
|
|
|
@ -12,14 +12,14 @@ lib.mkIf config.sane.impermanence.enable
|
|||
# /home/colin/foo/bar when stored in `private` is visible at
|
||||
# /home/colin/private/foo/bar
|
||||
prefix = "/home/colin";
|
||||
# fstab options inherited by all members of the store
|
||||
extraOptions = let
|
||||
defaultOrdering = let
|
||||
private-unit = config.sane.fs."/home/colin/private".unit;
|
||||
in [
|
||||
"noauto"
|
||||
# auto mount when ~/private is mounted
|
||||
"x-systemd.wanted-by=${private-unit}"
|
||||
];
|
||||
in {
|
||||
# auto create only after ~/private is mounted
|
||||
wantedBy = [ private-unit ];
|
||||
# we can't create things in private before local-fs.target
|
||||
wantedBeforeBy = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems."/home/colin/private" = {
|
||||
|
@ -27,6 +27,7 @@ lib.mkIf config.sane.impermanence.enable
|
|||
fsType = "fuse.gocryptfs";
|
||||
options = [
|
||||
"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`.
|
||||
"nodev"
|
||||
"nosuid"
|
||||
|
@ -36,20 +37,9 @@ lib.mkIf config.sane.impermanence.enable
|
|||
noCheck = true;
|
||||
};
|
||||
|
||||
sane.fs."/home/colin/private" = {
|
||||
# let sane.fs know that this corresponds to a fileSystems entry
|
||||
mount = {};
|
||||
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"
|
||||
];
|
||||
};
|
||||
# let sane.fs know about the endpoints
|
||||
sane.fs."/home/colin/private".mount = {};
|
||||
sane.fs."/nix/persist/home/colin/private".dir = {};
|
||||
|
||||
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
|
||||
system.fsPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
|
||||
|
|
Loading…
Reference in New Issue
Block a user