From 4685aa83395fdf9f70929a80420117392d137fe3 Mon Sep 17 00:00:00 2001 From: Francesco Zanini Date: Fri, 6 Jun 2025 15:59:11 +0200 Subject: [PATCH] Mount file-systems in the correct order Use the `fsBefore` function from NixOS to sort the file-systems. The function originates from `nixpkgs/nixos/lib/utils.nix`, but the file is a lambda that requires more arguments than we have available. --- lib/default.nix | 57 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/lib/default.nix b/lib/default.nix index 2a1e989f..f8d936bf 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -166,7 +166,6 @@ let _path: lhs: rhs: !(isAttrs lhs && isAttrs rhs) ) lhs rhs; - in recursiveMerge left right; @@ -357,6 +356,44 @@ let */ maybeStr = x: lib.optionalString (x != null) x; + /* + Check whenever `b` depends on `a` as a fileSystem + + Note: copied from nixpkgs/nixos/utils.nix @ 47718fe8858fa4642cd6a4f0b5a1173bad9ce8df + because the file that exports it is not meant to be used standalone. + */ + fsBefore = + a: b: + with lib; + let + # normalisePath adds a slash at the end of the path if it didn't already + # have one. + # + # The reason slashes are added at the end of each path is to prevent `b` + # from accidentally depending on `a` in cases like + # a = { mountPoint = "/aaa"; ... } + # b = { device = "/aaaa"; ... } + # Here a.mountPoint *is* a prefix of b.device even though a.mountPoint is + # *not* a parent of b.device. If we add a slash at the end of each string, + # though, this is not a problem: "/aaa/" is not a prefix of "/aaaa/". + normalisePath = path: "${path}${optionalString (!(hasSuffix "/" path)) "/"}"; + normalise = + mount: + mount + // { + device = normalisePath (toString mount.device); + mountPoint = normalisePath mount.mountPoint; + depends = map normalisePath mount.depends; + }; + + a' = normalise a; + b' = normalise b; + + in + hasPrefix a'.mountPoint b'.device + || hasPrefix a'.mountPoint b'.mountPoint + || any (hasPrefix a'.mountPoint) b'.depends; + /* Takes a Submodules config and options argument and returns a serializable subset of config variables as a shell script snippet. @@ -956,9 +993,25 @@ let default = with lib; let + mountOrder = pipe cfg.config._config.fileSystems.contents [ + (foldl' recursiveUpdate { }) + (mapAttrsToList ( + mountPoint: value: + value + // { + mountPoint = value.mountPoint or mountPoint; + depends = [ ]; + } + )) + (toposort diskoLib.fsBefore) + (getAttr "result") + (map (getAttr "mountPoint")) + ]; + fsMounts = diskoLib.deepMergeMap (dev: dev._mount.fs or { }) ( flatten (map attrValues (attrValues devices)) ); + sortedDeviceList = diskoLib.sortDevicesByDependencies (cfg.config._meta.deviceDependencies or { } ) devices; in @@ -968,7 +1021,7 @@ let ${concatMapStrings (dev: (attrByPath (dev ++ [ "_mount" ]) { } devices).dev or "") sortedDeviceList} # and then mount the filesystems in alphabetical order - ${concatStrings (attrValues fsMounts)} + ${concatStrings (attrVals mountOrder fsMounts)} ''; }; _unmount = lib.mkOption {