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

Changed files
+117 -41
nixos
modules
system
tasks
filesystems
tests
+1
nixos/modules/module-list.nix
···
./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
+3
nixos/modules/system/boot/stage-1-init.sh
···
# 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"!'
+21
nixos/modules/tasks/filesystems/erofs.nix
···
+
{ 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.
+
+
};
+
}
+92 -41
nixos/tests/non-default-filesystems.nix
···
-
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";
+
btrfs = makeTest
+
{
+
name = "non-default-filesystems-btrfs";
-
nodes.machine =
-
{ config, pkgs, lib, ... }:
-
let
-
disk = config.virtualisation.rootDevice;
-
in
-
{
-
virtualisation.rootDevice = "/dev/vda";
-
virtualisation.useDefaultFilesystems = false;
+
nodes.machine =
+
{ config, pkgs, lib, ... }:
+
let
+
disk = config.virtualisation.rootDevice;
+
in
+
{
+
virtualisation.rootDevice = "/dev/vda";
+
virtualisation.useDefaultFilesystems = false;
+
+
boot.initrd.availableKernelModules = [ "btrfs" ];
+
boot.supportedFilesystems = [ "btrfs" ];
+
+
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}
+
+
mkdir /nixos
+
mount -t btrfs ${disk} /nixos
+
+
${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root
+
${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home
-
boot.initrd.availableKernelModules = [ "btrfs" ];
-
boot.supportedFilesystems = [ "btrfs" ];
+
umount /nixos
+
fi
+
'';
-
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}
+
virtualisation.fileSystems = {
+
"/" = {
+
device = disk;
+
fsType = "btrfs";
+
options = [ "subvol=/root" ];
+
};
-
mkdir /nixos
-
mount -t btrfs ${disk} /nixos
+
"/home" = {
+
device = disk;
+
fsType = "btrfs";
+
options = [ "subvol=/home" ];
+
};
+
};
+
};
-
${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root
-
${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home
+
testScript = ''
+
machine.wait_for_unit("multi-user.target")
-
umount /nixos
-
fi
+
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" ];
-
};
+
erofs =
+
let
+
fsImage = "/tmp/non-default-filesystem.img";
+
in
+
makeTest {
+
name = "non-default-filesystems-erofs";
-
"/home" = {
-
device = disk;
-
fsType = "btrfs";
-
options = [ "subvol=/home" ];
+
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,
+
])
-
testScript = ''
-
machine.wait_for_unit("multi-user.target")
+
machine.start()
+
machine.wait_for_unit("default.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")
-
'';
-
})
+
file_contents = machine.succeed("cat /non-default/filesystem")
+
assert "erofs" in file_contents
+
'';
+
};
+
}