···256 reverseList = xs:
257 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
25800000000000000000000000000000000000000000000000000000000000000000000000000000000259 /* Sort a list based on a comparator function which compares two
260 elements and returns true if the first argument is strictly below
261 the second argument. The returned list is sorted in an increasing
···256 reverseList = xs:
257 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
258259+ /* Depth-First Search (DFS) for lists `list != []`.
260+261+ `before a b == true` means that `b` depends on `a` (there's an
262+ edge from `b` to `a`).
263+264+ Examples:
265+266+ listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ]
267+ == { minimal = "/"; # minimal element
268+ visited = [ "/home/user" ]; # seen elements (in reverse order)
269+ rest = [ "/home" "other" ]; # everything else
270+ }
271+272+ listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
273+ == { cycle = "/"; # cycle encountered at this element
274+ loops = [ "/" ]; # and continues to these elements
275+ visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order)
276+ rest = [ "/home" "other" ]; # everything else
277+278+ */
279+280+ listDfs = stopOnCycles: before: list:
281+ let
282+ dfs' = us: visited: rest:
283+ let
284+ c = filter (x: before x us) visited;
285+ b = partition (x: before x us) rest;
286+ in if stopOnCycles && (length c > 0)
287+ then { cycle = us; loops = c; inherit visited rest; }
288+ else if length b.right == 0
289+ then # nothing is before us
290+ { minimal = us; inherit visited rest; }
291+ else # grab the first one before us and continue
292+ dfs' (head b.right)
293+ ([ us ] ++ visited)
294+ (tail b.right ++ b.wrong);
295+ in dfs' (head list) [] (tail list);
296+297+ /* Sort a list based on a partial ordering using DFS. This
298+ implementation is O(N^2), if your ordering is linear, use `sort`
299+ instead.
300+301+ `before a b == true` means that `b` should be after `a`
302+ in the result.
303+304+ Examples:
305+306+ toposort hasPrefix [ "/home/user" "other" "/" "/home" ]
307+ == { result = [ "/" "/home" "/home/user" "other" ]; }
308+309+ toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
310+ == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle
311+ loops = [ "/" ]; } # loops back to these elements
312+313+ toposort hasPrefix [ "other" "/home/user" "/home" "/" ]
314+ == { result = [ "other" "/" "/home" "/home/user" ]; }
315+316+ toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; }
317+318+ */
319+320+ toposort = before: list:
321+ let
322+ dfsthis = listDfs true before list;
323+ toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
324+ in
325+ if length list < 2
326+ then # finish
327+ { result = list; }
328+ else if dfsthis ? "cycle"
329+ then # there's a cycle, starting from the current vertex, return it
330+ { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
331+ inherit (dfsthis) loops; }
332+ else if toporest ? "cycle"
333+ then # there's a cycle somewhere else in the graph, return it
334+ toporest
335+ # Slow, but short. Can be made a bit faster with an explicit stack.
336+ else # there are no cycles
337+ { result = [ dfsthis.minimal ] ++ toporest.result; };
338+339 /* Sort a list based on a comparator function which compares two
340 elements and returns true if the first argument is strictly below
341 the second argument. The returned list is sorted in an increasing
+9
nixos/lib/utils.nix
···23rec {
40000000005 # Escape a path according to the systemd rules, e.g. /dev/xyzzy
6 # becomes dev-xyzzy. FIXME: slow.
7 escapeSystemdPath = s:
···23rec {
45+ # Check whenever fileSystem is needed for boot
6+ fsNeededForBoot = fs: fs.neededForBoot
7+ || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ];
8+9+ # Check whenever `b` depends on `a` as a fileSystem
10+ # FIXME: it's incorrect to simply use hasPrefix here: "/dev/a" is not a parent of "/dev/ab"
11+ fsBefore = a: b: ((any (x: elem x [ "bind" "move" ]) b.options) && (a.mountPoint == b.device))
12+ || (hasPrefix a.mountPoint b.mountPoint);
13+14 # Escape a path according to the systemd rules, e.g. /dev/xyzzy
15 # becomes dev-xyzzy. FIXME: slow.
16 escapeSystemdPath = s:
···3# the modules necessary to mount the root file system, then calls the
4# init in the root file system to start the second boot stage.
56-{ config, lib, pkgs, ... }:
78with lib;
9···23 };
242500000026 # Some additional utilities needed in stage 1, like mount, lvm, fsck
27 # etc. We don't want to bring in all of those packages, so we just
28 # copy what we need. Instead of using statically linked binaries,
···71 ln -sf kmod $out/bin/modprobe
7273 # Copy resize2fs if needed.
74- ${optionalString (any (fs: fs.autoResize) (attrValues config.fileSystems)) ''
75 # We need mke2fs in the initrd.
76 copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
77 ''}
···126127 ${config.boot.initrd.extraUtilsCommandsTest}
128 ''; # */
129-130-131- # The initrd only has to mount / or any FS marked as necessary for
132- # booting (such as the FS containing /nix/store, or an FS needed for
133- # mounting /, like / on a loopback).
134- #
135- # We need to guarantee that / is the first filesystem in the list so
136- # that if and when lustrateRoot is invoked, nothing else is mounted
137- fileSystems = let
138- filterNeeded = filter
139- (fs: fs.mountPoint != "/" && (fs.neededForBoot || elem fs.mountPoint [ "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ]));
140- filterRoot = filter
141- (fs: fs.mountPoint == "/");
142- allFileSystems = attrValues config.fileSystems;
143- in (filterRoot allFileSystems) ++ (filterNeeded allFileSystems);
144145146 udevRules = pkgs.stdenv.mkDerivation {
···405 };
406407 config = mkIf (!config.boot.isContainer) {
408-409 assertions = [
410- { assertion = any (fs: fs.mountPoint == "/") (attrValues config.fileSystems);
411 message = "The ‘fileSystems’ option does not specify your root file system.";
412 }
413 { assertion = let inherit (config.boot) resumeDevice; in
···3# the modules necessary to mount the root file system, then calls the
4# init in the root file system to start the second boot stage.
56+{ config, lib, utils, pkgs, ... }:
78with lib;
9···23 };
242526+ # The initrd only has to mount `/` or any FS marked as necessary for
27+ # booting (such as the FS containing `/nix/store`, or an FS needed for
28+ # mounting `/`, like `/` on a loopback).
29+ fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
30+31+32 # Some additional utilities needed in stage 1, like mount, lvm, fsck
33 # etc. We don't want to bring in all of those packages, so we just
34 # copy what we need. Instead of using statically linked binaries,
···77 ln -sf kmod $out/bin/modprobe
7879 # Copy resize2fs if needed.
80+ ${optionalString (any (fs: fs.autoResize) fileSystems) ''
81 # We need mke2fs in the initrd.
82 copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
83 ''}
···132133 ${config.boot.initrd.extraUtilsCommandsTest}
134 ''; # */
000000000000000135136137 udevRules = pkgs.stdenv.mkDerivation {
···396 };
397398 config = mkIf (!config.boot.isContainer) {
0399 assertions = [
400+ { assertion = any (fs: fs.mountPoint == "/") fileSystems;
401 message = "The ‘fileSystems’ option does not specify your root file system.";
402 }
403 { assertion = let inherit (config.boot) resumeDevice; in