From fa09e0a3c7cc91b766b0bd34fcfe51b69bd8cca6 Mon Sep 17 00:00:00 2001 From: nikstur Date: Thu, 11 May 2023 14:35:00 +0200 Subject: [PATCH] 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. --- nixos/modules/module-list.nix | 1 + nixos/modules/system/boot/stage-1-init.sh | 3 + nixos/modules/tasks/filesystems/erofs.nix | 21 ++++ nixos/tests/non-default-filesystems.nix | 139 +++++++++++++++------- 4 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 nixos/modules/tasks/filesystems/erofs.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 4941b78a5772..6d3588d11ccf 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1370,6 +1370,7 @@ ./tasks/filesystems/cifs.nix ./tasks/filesystems/ecryptfs.nix ./tasks/filesystems/envfs.nix + ./tasks/filesystems/erofs.nix ./tasks/filesystems/exfat.nix ./tasks/filesystems/ext.nix ./tasks/filesystems/f2fs.nix diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index 835788dbbc97..387c27d86ebb 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -293,6 +293,9 @@ checkFS() { # Skip fsck for inherently readonly filesystems. if [ "$fsType" = squashfs ]; then return 0; fi + # Skip fsck.erofs because it is still experimental. + if [ "$fsType" = erofs ]; then return 0; fi + # If we couldn't figure out the FS type, then skip fsck. if [ "$fsType" = auto ]; then echo 'cannot check filesystem with type "auto"!' diff --git a/nixos/modules/tasks/filesystems/erofs.nix b/nixos/modules/tasks/filesystems/erofs.nix new file mode 100644 index 000000000000..a3d657669350 --- /dev/null +++ b/nixos/modules/tasks/filesystems/erofs.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: + +let + + inInitrd = lib.any (fs: fs == "erofs") config.boot.initrd.supportedFilesystems; + inSystem = lib.any (fs: fs == "erofs") config.boot.supportedFilesystems; + +in + +{ + config = lib.mkIf (inInitrd || inSystem) { + + system.fsPackages = [ pkgs.erofs-utils ]; + + boot.initrd.availableKernelModules = lib.mkIf inInitrd [ "erofs" ]; + + # fsck.erofs is currently experimental and should not be run as a + # privileged user. Thus, it is not included in the initrd. + + }; +} diff --git a/nixos/tests/non-default-filesystems.nix b/nixos/tests/non-default-filesystems.nix index d4e8bfbc65e9..03cc5bf709a4 100644 --- a/nixos/tests/non-default-filesystems.nix +++ b/nixos/tests/non-default-filesystems.nix @@ -1,55 +1,106 @@ -import ./make-test-python.nix ({ lib, pkgs, ... }: +{ system ? builtins.currentSystem +, config ? { } +, pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing-python.nix { inherit system pkgs; }; +with pkgs.lib; { - name = "non-default-filesystems"; - - nodes.machine = - { config, pkgs, lib, ... }: - let - disk = config.virtualisation.rootDevice; - in + btrfs = makeTest { - virtualisation.rootDevice = "/dev/vda"; - virtualisation.useDefaultFilesystems = false; + name = "non-default-filesystems-btrfs"; - boot.initrd.availableKernelModules = [ "btrfs" ]; - boot.supportedFilesystems = [ "btrfs" ]; + nodes.machine = + { config, pkgs, lib, ... }: + let + disk = config.virtualisation.rootDevice; + in + { + virtualisation.rootDevice = "/dev/vda"; + virtualisation.useDefaultFilesystems = false; - boot.initrd.postDeviceCommands = '' - FSTYPE=$(blkid -o value -s TYPE ${disk} || true) - if test -z "$FSTYPE"; then - modprobe btrfs - ${pkgs.btrfs-progs}/bin/mkfs.btrfs ${disk} + boot.initrd.availableKernelModules = [ "btrfs" ]; + boot.supportedFilesystems = [ "btrfs" ]; - mkdir /nixos - mount -t btrfs ${disk} /nixos + boot.initrd.postDeviceCommands = '' + FSTYPE=$(blkid -o value -s TYPE ${disk} || true) + if test -z "$FSTYPE"; then + modprobe btrfs + ${pkgs.btrfs-progs}/bin/mkfs.btrfs ${disk} - ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root - ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home + mkdir /nixos + mount -t btrfs ${disk} /nixos - umount /nixos - fi + ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root + ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home + + umount /nixos + fi + ''; + + virtualisation.fileSystems = { + "/" = { + device = disk; + fsType = "btrfs"; + options = [ "subvol=/root" ]; + }; + + "/home" = { + device = disk; + fsType = "btrfs"; + options = [ "subvol=/home" ]; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + + with subtest("BTRFS filesystems are mounted correctly"): + machine.succeed("grep -E '/dev/vda / btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/root 0 0' /proc/mounts") + machine.succeed("grep -E '/dev/vda /home btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/home 0 0' /proc/mounts") ''; - - virtualisation.fileSystems = { - "/" = { - device = disk; - fsType = "btrfs"; - options = [ "subvol=/root" ]; - }; - - "/home" = { - device = disk; - fsType = "btrfs"; - options = [ "subvol=/home" ]; - }; - }; }; - testScript = '' - machine.wait_for_unit("multi-user.target") + erofs = + let + fsImage = "/tmp/non-default-filesystem.img"; + in + makeTest { + name = "non-default-filesystems-erofs"; - with subtest("BTRFS filesystems are mounted correctly"): - machine.succeed("grep -E '/dev/vda / btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/root 0 0' /proc/mounts") - machine.succeed("grep -E '/dev/vda /home btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/home 0 0' /proc/mounts") - ''; -}) + nodes.machine = _: { + virtualisation.qemu.drives = [{ + name = "non-default-filesystem"; + file = fsImage; + }]; + + virtualisation.fileSystems."/non-default" = { + device = "/dev/vdb"; + fsType = "erofs"; + neededForBoot = true; + }; + }; + + testScript = '' + import subprocess + import tempfile + + with tempfile.TemporaryDirectory() as tmp_dir: + with open(f"{tmp_dir}/filesystem", "w") as f: + f.write("erofs") + + subprocess.run([ + "${pkgs.erofs-utils}/bin/mkfs.erofs", + "${fsImage}", + tmp_dir, + ]) + + machine.start() + machine.wait_for_unit("default.target") + + file_contents = machine.succeed("cat /non-default/filesystem") + assert "erofs" in file_contents + ''; + }; +}