lol

nixos/filesystems: init erofs

Enable using an erofs filesystem as one of the filesystems needed to
boot the system. This is useful for example in image based deployments
where the Nix store is mounted read only.
[erofs](https://docs.kernel.org/filesystems/erofs.html) offers multiple
benefits over older filesystems like squashfs. Skip fsck.erofs because
it is still experimental.

nikstur fa09e0a3 b15daed9

+117 -41
+1
nixos/modules/module-list.nix
··· 1370 1370 ./tasks/filesystems/cifs.nix 1371 1371 ./tasks/filesystems/ecryptfs.nix 1372 1372 ./tasks/filesystems/envfs.nix 1373 + ./tasks/filesystems/erofs.nix 1373 1374 ./tasks/filesystems/exfat.nix 1374 1375 ./tasks/filesystems/ext.nix 1375 1376 ./tasks/filesystems/f2fs.nix
+3
nixos/modules/system/boot/stage-1-init.sh
··· 293 293 # Skip fsck for inherently readonly filesystems. 294 294 if [ "$fsType" = squashfs ]; then return 0; fi 295 295 296 + # Skip fsck.erofs because it is still experimental. 297 + if [ "$fsType" = erofs ]; then return 0; fi 298 + 296 299 # If we couldn't figure out the FS type, then skip fsck. 297 300 if [ "$fsType" = auto ]; then 298 301 echo 'cannot check filesystem with type "auto"!'
+21
nixos/modules/tasks/filesystems/erofs.nix
··· 1 + { config, lib, pkgs, ... }: 2 + 3 + let 4 + 5 + inInitrd = lib.any (fs: fs == "erofs") config.boot.initrd.supportedFilesystems; 6 + inSystem = lib.any (fs: fs == "erofs") config.boot.supportedFilesystems; 7 + 8 + in 9 + 10 + { 11 + config = lib.mkIf (inInitrd || inSystem) { 12 + 13 + system.fsPackages = [ pkgs.erofs-utils ]; 14 + 15 + boot.initrd.availableKernelModules = lib.mkIf inInitrd [ "erofs" ]; 16 + 17 + # fsck.erofs is currently experimental and should not be run as a 18 + # privileged user. Thus, it is not included in the initrd. 19 + 20 + }; 21 + }
+92 -41
nixos/tests/non-default-filesystems.nix
··· 1 - import ./make-test-python.nix ({ lib, pkgs, ... }: 1 + { system ? builtins.currentSystem 2 + , config ? { } 3 + , pkgs ? import ../.. { inherit system config; } 4 + }: 5 + 6 + with import ../lib/testing-python.nix { inherit system pkgs; }; 7 + with pkgs.lib; 2 8 { 3 - name = "non-default-filesystems"; 9 + btrfs = makeTest 10 + { 11 + name = "non-default-filesystems-btrfs"; 4 12 5 - nodes.machine = 6 - { config, pkgs, lib, ... }: 7 - let 8 - disk = config.virtualisation.rootDevice; 9 - in 10 - { 11 - virtualisation.rootDevice = "/dev/vda"; 12 - virtualisation.useDefaultFilesystems = false; 13 + nodes.machine = 14 + { config, pkgs, lib, ... }: 15 + let 16 + disk = config.virtualisation.rootDevice; 17 + in 18 + { 19 + virtualisation.rootDevice = "/dev/vda"; 20 + virtualisation.useDefaultFilesystems = false; 21 + 22 + boot.initrd.availableKernelModules = [ "btrfs" ]; 23 + boot.supportedFilesystems = [ "btrfs" ]; 24 + 25 + boot.initrd.postDeviceCommands = '' 26 + FSTYPE=$(blkid -o value -s TYPE ${disk} || true) 27 + if test -z "$FSTYPE"; then 28 + modprobe btrfs 29 + ${pkgs.btrfs-progs}/bin/mkfs.btrfs ${disk} 30 + 31 + mkdir /nixos 32 + mount -t btrfs ${disk} /nixos 33 + 34 + ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root 35 + ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home 13 36 14 - boot.initrd.availableKernelModules = [ "btrfs" ]; 15 - boot.supportedFilesystems = [ "btrfs" ]; 37 + umount /nixos 38 + fi 39 + ''; 16 40 17 - boot.initrd.postDeviceCommands = '' 18 - FSTYPE=$(blkid -o value -s TYPE ${disk} || true) 19 - if test -z "$FSTYPE"; then 20 - modprobe btrfs 21 - ${pkgs.btrfs-progs}/bin/mkfs.btrfs ${disk} 41 + virtualisation.fileSystems = { 42 + "/" = { 43 + device = disk; 44 + fsType = "btrfs"; 45 + options = [ "subvol=/root" ]; 46 + }; 22 47 23 - mkdir /nixos 24 - mount -t btrfs ${disk} /nixos 48 + "/home" = { 49 + device = disk; 50 + fsType = "btrfs"; 51 + options = [ "subvol=/home" ]; 52 + }; 53 + }; 54 + }; 25 55 26 - ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root 27 - ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home 56 + testScript = '' 57 + machine.wait_for_unit("multi-user.target") 28 58 29 - umount /nixos 30 - fi 59 + with subtest("BTRFS filesystems are mounted correctly"): 60 + machine.succeed("grep -E '/dev/vda / btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/root 0 0' /proc/mounts") 61 + machine.succeed("grep -E '/dev/vda /home btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/home 0 0' /proc/mounts") 31 62 ''; 63 + }; 32 64 33 - virtualisation.fileSystems = { 34 - "/" = { 35 - device = disk; 36 - fsType = "btrfs"; 37 - options = [ "subvol=/root" ]; 38 - }; 65 + erofs = 66 + let 67 + fsImage = "/tmp/non-default-filesystem.img"; 68 + in 69 + makeTest { 70 + name = "non-default-filesystems-erofs"; 39 71 40 - "/home" = { 41 - device = disk; 42 - fsType = "btrfs"; 43 - options = [ "subvol=/home" ]; 72 + nodes.machine = _: { 73 + virtualisation.qemu.drives = [{ 74 + name = "non-default-filesystem"; 75 + file = fsImage; 76 + }]; 77 + 78 + virtualisation.fileSystems."/non-default" = { 79 + device = "/dev/vdb"; 80 + fsType = "erofs"; 81 + neededForBoot = true; 44 82 }; 45 83 }; 46 - }; 84 + 85 + testScript = '' 86 + import subprocess 87 + import tempfile 88 + 89 + with tempfile.TemporaryDirectory() as tmp_dir: 90 + with open(f"{tmp_dir}/filesystem", "w") as f: 91 + f.write("erofs") 92 + 93 + subprocess.run([ 94 + "${pkgs.erofs-utils}/bin/mkfs.erofs", 95 + "${fsImage}", 96 + tmp_dir, 97 + ]) 47 98 48 - testScript = '' 49 - machine.wait_for_unit("multi-user.target") 99 + machine.start() 100 + machine.wait_for_unit("default.target") 50 101 51 - with subtest("BTRFS filesystems are mounted correctly"): 52 - machine.succeed("grep -E '/dev/vda / btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/root 0 0' /proc/mounts") 53 - machine.succeed("grep -E '/dev/vda /home btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/home 0 0' /proc/mounts") 54 - ''; 55 - }) 102 + file_contents = machine.succeed("cat /non-default/filesystem") 103 + assert "erofs" in file_contents 104 + ''; 105 + }; 106 + }