at 23.11-beta 5.6 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3let 4 5 bootFs = lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (utils.fsNeededForBoot fs)) config.fileSystems; 6 7 commonFunctions = '' 8 prompt() { 9 local name="$1" 10 printf "enter passphrase for $name: " 11 } 12 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') 19 20 printf "waiting for device to appear $path" 21 for try in $(seq 10); do 22 if [ -e $path ]; then 23 success=true 24 break 25 else 26 target=$(blkid --uuid $uuid) 27 if [ $? == 0 ]; then 28 success=true 29 break 30 fi 31 fi 32 echo -n "." 33 sleep 1 34 done 35 printf "\n" 36 if [ $success == true ]; then 37 path=$target 38 fi 39 40 if bcachefs unlock -c $path > /dev/null 2> /dev/null; then # test for encryption 41 prompt $name 42 until bcachefs unlock $path 2> /dev/null; do # repeat until successfully unlocked 43 printf "unlocking failed!\n" 44 prompt $name 45 done 46 printf "unlocking successful.\n" 47 else 48 echo "Cannot unlock device $uuid with path $path" >&2 49 fi 50 } 51 ''; 52 53 # we need only unlock one device manually, and cannot pass multiple at once 54 # remove this adaptation when bcachefs implements mounting by filesystem uuid 55 # also, implement automatic waiting for the constituent devices when that happens 56 # bcachefs does not support mounting devices with colons in the path, ergo we don't (see #49671) 57 firstDevice = fs: lib.head (lib.splitString ":" fs.device); 58 59 openCommand = name: fs: '' 60 tryUnlock ${name} ${firstDevice fs} 61 ''; 62 63 mkUnits = prefix: name: fs: let 64 mountUnit = "${utils.escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint))}.mount"; 65 device = firstDevice fs; 66 deviceUnit = "${utils.escapeSystemdPath device}.device"; 67 in { 68 name = "unlock-bcachefs-${utils.escapeSystemdPath fs.mountPoint}"; 69 value = { 70 description = "Unlock bcachefs for ${fs.mountPoint}"; 71 requiredBy = [ mountUnit ]; 72 before = [ mountUnit ]; 73 bindsTo = [ deviceUnit ]; 74 after = [ deviceUnit ]; 75 unitConfig.DefaultDependencies = false; 76 serviceConfig = { 77 Type = "oneshot"; 78 ExecCondition = "${pkgs.bcachefs-tools}/bin/bcachefs unlock -c \"${device}\""; 79 Restart = "on-failure"; 80 RestartMode = "direct"; 81 # Ideally, this service would lock the key on stop. 82 # As is, RemainAfterExit doesn't accomplish anything. 83 RemainAfterExit = true; 84 }; 85 script = '' 86 ${config.boot.initrd.systemd.package}/bin/systemd-ask-password --timeout=0 "enter passphrase for ${name}" | exec ${pkgs.bcachefs-tools}/bin/bcachefs unlock "${device}" 87 ''; 88 }; 89 }; 90 91 assertions = [ 92 { 93 assertion = let 94 kernel = config.boot.kernelPackages.kernel; 95 in ( 96 kernel.kernelAtLeast "6.7" || ( 97 lib.elem (kernel.structuredExtraConfig.BCACHEFS_FS or null) [ 98 lib.kernel.module 99 lib.kernel.yes 100 lib.kernel.option.yes 101 ] 102 ) 103 ); 104 105 message = "Linux 6.7-rc1 at minimum or a custom linux kernel with bcachefs support is required"; 106 } 107 ]; 108in 109 110{ 111 config = lib.mkIf (lib.elem "bcachefs" config.boot.supportedFilesystems) (lib.mkMerge [ 112 { 113 inherit assertions; 114 # needed for systemd-remount-fs 115 system.fsPackages = [ pkgs.bcachefs-tools ]; 116 117 # FIXME: Replace this with `linuxPackages_testing` after NixOS 23.11 is released 118 # FIXME: Replace this with `linuxPackages_latest` when 6.7 is released, remove this line when the LTS version is at least 6.7 119 boot.kernelPackages = lib.mkDefault ( 120 # FIXME: Remove warning after NixOS 23.11 is released 121 lib.warn "Please upgrade to Linux 6.7-rc1 or later: 'linuxPackages_testing_bcachefs' is deprecated. Use 'boot.kernelPackages = pkgs.linuxPackages_testing;' to silence this warning" 122 pkgs.linuxPackages_testing_bcachefs 123 ); 124 125 systemd.services = lib.mapAttrs' (mkUnits "") (lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (!utils.fsNeededForBoot fs)) config.fileSystems); 126 } 127 128 (lib.mkIf ((lib.elem "bcachefs" config.boot.initrd.supportedFilesystems) || (bootFs != {})) { 129 inherit assertions; 130 # chacha20 and poly1305 are required only for decryption attempts 131 boot.initrd.availableKernelModules = [ "bcachefs" "sha256" "chacha20" "poly1305" ]; 132 boot.initrd.systemd.extraBin = { 133 # do we need this? boot/systemd.nix:566 & boot/systemd/initrd.nix:357 134 "bcachefs" = "${pkgs.bcachefs-tools}/bin/bcachefs"; 135 "mount.bcachefs" = "${pkgs.bcachefs-tools}/bin/mount.bcachefs"; 136 }; 137 boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) '' 138 copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/bcachefs 139 copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/mount.bcachefs 140 ''; 141 boot.initrd.extraUtilsCommandsTest = lib.mkIf (!config.boot.initrd.systemd.enable) '' 142 $out/bin/bcachefs version 143 ''; 144 145 boot.initrd.postDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + lib.concatStrings (lib.mapAttrsToList openCommand bootFs)); 146 147 boot.initrd.systemd.services = lib.mapAttrs' (mkUnits "/sysroot") bootFs; 148 }) 149 ]); 150}