grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / tasks / filesystems / bcachefs.nix
blobd7e83464391c4b75444dc1fc545049573dcd2807
1 { config, lib, pkgs, utils, ... }:
3 let
5   bootFs = lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (utils.fsNeededForBoot fs)) config.fileSystems;
7   commonFunctions = ''
8     prompt() {
9         local name="$1"
10         printf "enter passphrase for $name: "
11     }
13     tryUnlock() {
14         local name="$1"
15         local path="$2"
16         local success=false
17         local target
18         local uuid=$(echo -n $path | sed -e 's,UUID=\(.*\),\1,g')
20         printf "waiting for device to appear $path"
21         for try in $(seq 10); do
22           if [ -e $path ]; then
23               target=$(readlink -f $path)
24               success=true
25               break
26           else
27               target=$(blkid --uuid $uuid)
28               if [ $? == 0 ]; then
29                  success=true
30                  break
31               fi
32           fi
33           echo -n "."
34           sleep 1
35         done
36         printf "\n"
37         if [ $success == true ]; then
38             path=$target
39         fi
41         if bcachefs unlock -c $path > /dev/null 2> /dev/null; then    # test for encryption
42             prompt $name
43             until bcachefs unlock $path 2> /dev/null; do              # repeat until successfully unlocked
44                 printf "unlocking failed!\n"
45                 prompt $name
46             done
47             printf "unlocking successful.\n"
48         else
49             echo "Cannot unlock device $uuid with path $path" >&2
50         fi
51     }
52   '';
54   # we need only unlock one device manually, and cannot pass multiple at once
55   # remove this adaptation when bcachefs implements mounting by filesystem uuid
56   # also, implement automatic waiting for the constituent devices when that happens
57   # bcachefs does not support mounting devices with colons in the path, ergo we don't (see #49671)
58   firstDevice = fs: lib.head (lib.splitString ":" fs.device);
60   useClevis = fs: config.boot.initrd.clevis.enable && (lib.hasAttr (firstDevice fs) config.boot.initrd.clevis.devices);
62   openCommand = name: fs: if useClevis fs then ''
63     if clevis decrypt < /etc/clevis/${firstDevice fs}.jwe | bcachefs unlock ${firstDevice fs}
64     then
65       printf "unlocked ${name} using clevis\n"
66     else
67       printf "falling back to interactive unlocking...\n"
68       tryUnlock ${name} ${firstDevice fs}
69     fi
70   '' else ''
71     tryUnlock ${name} ${firstDevice fs}
72   '';
74   mkUnits = prefix: name: fs: let
75     mountUnit = "${utils.escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint))}.mount";
76     device = firstDevice fs;
77     deviceUnit = "${utils.escapeSystemdPath device}.device";
78   in {
79     name = "unlock-bcachefs-${utils.escapeSystemdPath fs.mountPoint}";
80     value = {
81       description = "Unlock bcachefs for ${fs.mountPoint}";
82       requiredBy = [ mountUnit ];
83       after = [ deviceUnit ];
84       before = [ mountUnit "shutdown.target" ];
85       bindsTo = [ deviceUnit ];
86       conflicts = [ "shutdown.target" ];
87       unitConfig.DefaultDependencies = false;
88       serviceConfig = {
89         Type = "oneshot";
90         ExecCondition = "${pkgs.bcachefs-tools}/bin/bcachefs unlock -c \"${device}\"";
91         Restart = "on-failure";
92         RestartMode = "direct";
93         # Ideally, this service would lock the key on stop.
94         # As is, RemainAfterExit doesn't accomplish anything.
95         RemainAfterExit = true;
96       };
97       script = let
98         unlock = ''${pkgs.bcachefs-tools}/bin/bcachefs unlock "${device}"'';
99         unlockInteractively = ''${config.boot.initrd.systemd.package}/bin/systemd-ask-password --timeout=0 "enter passphrase for ${name}" | exec ${unlock}'';
100       in if useClevis fs then ''
101         if ${config.boot.initrd.clevis.package}/bin/clevis decrypt < "/etc/clevis/${device}.jwe" | ${unlock}
102         then
103           printf "unlocked ${name} using clevis\n"
104         else
105           printf "falling back to interactive unlocking...\n"
106           ${unlockInteractively}
107         fi
108       '' else ''
109         ${unlockInteractively}
110       '';
111     };
112   };
114   assertions = [
115     {
116       assertion = let
117         kernel = config.boot.kernelPackages.kernel;
118       in (
119         kernel.kernelAtLeast "6.7" || (
120           lib.elem (kernel.structuredExtraConfig.BCACHEFS_FS or null) [
121             lib.kernel.module
122             lib.kernel.yes
123             (lib.kernel.option lib.kernel.yes)
124           ]
125         )
126       );
128       message = "Linux 6.7-rc1 at minimum or a custom linux kernel with bcachefs support is required";
129     }
130   ];
134   config = lib.mkIf (config.boot.supportedFilesystems.bcachefs or false) (lib.mkMerge [
135     {
136       inherit assertions;
137       # needed for systemd-remount-fs
138       system.fsPackages = [ pkgs.bcachefs-tools ];
139       # FIXME: Remove this line when the LTS (default) kernel is at least version 6.7
140       boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
141       services.udev.packages = [ pkgs.bcachefs-tools ];
143       systemd = {
144         packages = [ pkgs.bcachefs-tools ];
145         services = lib.mapAttrs' (mkUnits "") (lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (!utils.fsNeededForBoot fs)) config.fileSystems);
146       };
147     }
149     (lib.mkIf ((config.boot.initrd.supportedFilesystems.bcachefs or false) || (bootFs != {})) {
150       inherit assertions;
151       # chacha20 and poly1305 are required only for decryption attempts
152       boot.initrd.availableKernelModules = [ "bcachefs" "sha256" "chacha20" "poly1305" ];
153       boot.initrd.systemd.extraBin = {
154         # do we need this? boot/systemd.nix:566 & boot/systemd/initrd.nix:357
155         "bcachefs" = "${pkgs.bcachefs-tools}/bin/bcachefs";
156         "mount.bcachefs" = "${pkgs.bcachefs-tools}/bin/mount.bcachefs";
157       };
158       boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
159         copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/bcachefs
160         copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/mount.bcachefs
161       '';
162       boot.initrd.extraUtilsCommandsTest = lib.mkIf (!config.boot.initrd.systemd.enable) ''
163         $out/bin/bcachefs version
164       '';
166       boot.initrd.postDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + lib.concatStrings (lib.mapAttrsToList openCommand bootFs));
168       boot.initrd.systemd.services = lib.mapAttrs' (mkUnits "/sysroot") bootFs;
169     })
170   ]);