···256256 reverseList = xs:
257257 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
258258259259+ /* Depth-First Search (DFS) for lists `list != []`.
260260+261261+ `before a b == true` means that `b` depends on `a` (there's an
262262+ edge from `b` to `a`).
263263+264264+ Examples:
265265+266266+ listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ]
267267+ == { minimal = "/"; # minimal element
268268+ visited = [ "/home/user" ]; # seen elements (in reverse order)
269269+ rest = [ "/home" "other" ]; # everything else
270270+ }
271271+272272+ listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
273273+ == { cycle = "/"; # cycle encountered at this element
274274+ loops = [ "/" ]; # and continues to these elements
275275+ visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order)
276276+ rest = [ "/home" "other" ]; # everything else
277277+278278+ */
279279+280280+ listDfs = stopOnCycles: before: list:
281281+ let
282282+ dfs' = us: visited: rest:
283283+ let
284284+ c = filter (x: before x us) visited;
285285+ b = partition (x: before x us) rest;
286286+ in if stopOnCycles && (length c > 0)
287287+ then { cycle = us; loops = c; inherit visited rest; }
288288+ else if length b.right == 0
289289+ then # nothing is before us
290290+ { minimal = us; inherit visited rest; }
291291+ else # grab the first one before us and continue
292292+ dfs' (head b.right)
293293+ ([ us ] ++ visited)
294294+ (tail b.right ++ b.wrong);
295295+ in dfs' (head list) [] (tail list);
296296+297297+ /* Sort a list based on a partial ordering using DFS. This
298298+ implementation is O(N^2), if your ordering is linear, use `sort`
299299+ instead.
300300+301301+ `before a b == true` means that `b` should be after `a`
302302+ in the result.
303303+304304+ Examples:
305305+306306+ toposort hasPrefix [ "/home/user" "other" "/" "/home" ]
307307+ == { result = [ "/" "/home" "/home/user" "other" ]; }
308308+309309+ toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
310310+ == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle
311311+ loops = [ "/" ]; } # loops back to these elements
312312+313313+ toposort hasPrefix [ "other" "/home/user" "/home" "/" ]
314314+ == { result = [ "other" "/" "/home" "/home/user" ]; }
315315+316316+ toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; }
317317+318318+ */
319319+320320+ toposort = before: list:
321321+ let
322322+ dfsthis = listDfs true before list;
323323+ toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
324324+ in
325325+ if length list < 2
326326+ then # finish
327327+ { result = list; }
328328+ else if dfsthis ? "cycle"
329329+ then # there's a cycle, starting from the current vertex, return it
330330+ { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
331331+ inherit (dfsthis) loops; }
332332+ else if toporest ? "cycle"
333333+ then # there's a cycle somewhere else in the graph, return it
334334+ toporest
335335+ # Slow, but short. Can be made a bit faster with an explicit stack.
336336+ else # there are no cycles
337337+ { result = [ dfsthis.minimal ] ++ toporest.result; };
338338+259339 /* Sort a list based on a comparator function which compares two
260340 elements and returns true if the first argument is strictly below
261341 the second argument. The returned list is sorted in an increasing
+9
nixos/lib/utils.nix
···2233rec {
4455+ # Check whenever fileSystem is needed for boot
66+ fsNeededForBoot = fs: fs.neededForBoot
77+ || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ];
88+99+ # Check whenever `b` depends on `a` as a fileSystem
1010+ # FIXME: it's incorrect to simply use hasPrefix here: "/dev/a" is not a parent of "/dev/ab"
1111+ fsBefore = a: b: ((any (x: elem x [ "bind" "move" ]) b.options) && (a.mountPoint == b.device))
1212+ || (hasPrefix a.mountPoint b.mountPoint);
1313+514 # Escape a path according to the systemd rules, e.g. /dev/xyzzy
615 # becomes dev-xyzzy. FIXME: slow.
716 escapeSystemdPath = s:
···33# the modules necessary to mount the root file system, then calls the
44# init in the root file system to start the second boot stage.
5566-{ config, lib, pkgs, ... }:
66+{ config, lib, utils, pkgs, ... }:
7788with lib;
99···2323 };
242425252626+ # The initrd only has to mount `/` or any FS marked as necessary for
2727+ # booting (such as the FS containing `/nix/store`, or an FS needed for
2828+ # mounting `/`, like `/` on a loopback).
2929+ fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
3030+3131+2632 # Some additional utilities needed in stage 1, like mount, lvm, fsck
2733 # etc. We don't want to bring in all of those packages, so we just
2834 # copy what we need. Instead of using statically linked binaries,
···7177 ln -sf kmod $out/bin/modprobe
72787379 # Copy resize2fs if needed.
7474- ${optionalString (any (fs: fs.autoResize) (attrValues config.fileSystems)) ''
8080+ ${optionalString (any (fs: fs.autoResize) fileSystems) ''
7581 # We need mke2fs in the initrd.
7682 copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
7783 ''}
···126132127133 ${config.boot.initrd.extraUtilsCommandsTest}
128134 ''; # */
129129-130130-131131- # The initrd only has to mount / or any FS marked as necessary for
132132- # booting (such as the FS containing /nix/store, or an FS needed for
133133- # mounting /, like / on a loopback).
134134- #
135135- # We need to guarantee that / is the first filesystem in the list so
136136- # that if and when lustrateRoot is invoked, nothing else is mounted
137137- fileSystems = let
138138- filterNeeded = filter
139139- (fs: fs.mountPoint != "/" && (fs.neededForBoot || elem fs.mountPoint [ "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ]));
140140- filterRoot = filter
141141- (fs: fs.mountPoint == "/");
142142- allFileSystems = attrValues config.fileSystems;
143143- in (filterRoot allFileSystems) ++ (filterNeeded allFileSystems);
144135145136146137 udevRules = pkgs.stdenv.mkDerivation {
···405396 };
406397407398 config = mkIf (!config.boot.isContainer) {
408408-409399 assertions = [
410410- { assertion = any (fs: fs.mountPoint == "/") (attrValues config.fileSystems);
400400+ { assertion = any (fs: fs.mountPoint == "/") fileSystems;
411401 message = "The ‘fileSystems’ option does not specify your root file system.";
412402 }
413403 { assertion = let inherit (config.boot) resumeDevice; in