diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml index 8c4bc735519..0954017c98f 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml @@ -79,6 +79,12 @@ services.filebeat. + + + apfs, + a kernel module for mounting the Apple File System (APFS). + + FRRouting, a diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md index cfff3afd81c..b47d4ef470b 100644 --- a/nixos/doc/manual/release-notes/rl-2205.section.md +++ b/nixos/doc/manual/release-notes/rl-2205.section.md @@ -27,6 +27,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html), a lightweight shipper for forwarding and centralizing log data. Available as [services.filebeat](#opt-services.filebeat.enable). +- [apfs](https://github.com/linux-apfs/linux-apfs-rw), a kernel module for mounting the Apple File System (APFS). + - [FRRouting](https://frrouting.org/), a popular suite of Internet routing protocol daemons (BGP, BFD, OSPF, IS-IS, VVRP and others). Available as [services.frr](#opt-services.ffr.babel.enable) - [heisenbridge](https://github.com/hifi/heisenbridge), a bouncer-style Matrix IRC bridge. Available as [services.heisenbridge](options.html#opt-services.heisenbridge.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 7593a5037db..a6683268839 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1164,6 +1164,7 @@ ./tasks/cpu-freq.nix ./tasks/encrypted-devices.nix ./tasks/filesystems.nix + ./tasks/filesystems/apfs.nix ./tasks/filesystems/bcachefs.nix ./tasks/filesystems/btrfs.nix ./tasks/filesystems/cifs.nix diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index f86d4641228..8fcc1f02972 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -282,6 +282,9 @@ checkFS() { # Don't check resilient COWs as they validate the fs structures at mount time if [ "$fsType" = btrfs -o "$fsType" = zfs -o "$fsType" = bcachefs ]; then return 0; fi + # Skip fsck for apfs as the fsck utility does not support repairing the filesystem (no -a option) + if [ "$fsType" = apfs ]; then return 0; fi + # Skip fsck for nilfs2 - not needed by design and no fsck tool for this filesystem. if [ "$fsType" = nilfs2 ]; then return 0; fi diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index 225bcbe58e0..f3da6771197 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -250,7 +250,7 @@ in environment.etc.fstab.text = let - fsToSkipCheck = [ "none" "bindfs" "btrfs" "zfs" "tmpfs" "nfs" "vboxsf" "glusterfs" ]; + fsToSkipCheck = [ "none" "bindfs" "btrfs" "zfs" "tmpfs" "nfs" "vboxsf" "glusterfs" "apfs" ]; skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck; # https://wiki.archlinux.org/index.php/fstab#Filepath_spaces escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string; diff --git a/nixos/modules/tasks/filesystems/apfs.nix b/nixos/modules/tasks/filesystems/apfs.nix new file mode 100644 index 00000000000..2f2be351df6 --- /dev/null +++ b/nixos/modules/tasks/filesystems/apfs.nix @@ -0,0 +1,22 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inInitrd = any (fs: fs == "apfs") config.boot.initrd.supportedFilesystems; + +in + +{ + config = mkIf (any (fs: fs == "apfs") config.boot.supportedFilesystems) { + + system.fsPackages = [ pkgs.apfsprogs ]; + + boot.extraModulePackages = [ config.boot.kernelPackages.apfs ]; + + boot.initrd.kernelModules = mkIf inInitrd [ "apfs" ]; + + # Don't copy apfsck into the initramfs since it does not support repairing the filesystem + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index e0b2608b362..b75cbf4d10b 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -35,6 +35,7 @@ in agda = handleTest ./agda.nix {}; airsonic = handleTest ./airsonic.nix {}; amazon-init-shell = handleTest ./amazon-init-shell.nix {}; + apfs = handleTest ./apfs.nix {}; apparmor = handleTest ./apparmor.nix {}; atd = handleTest ./atd.nix {}; atop = handleTest ./atop.nix {}; diff --git a/nixos/tests/apfs.nix b/nixos/tests/apfs.nix new file mode 100644 index 00000000000..a82886cbe73 --- /dev/null +++ b/nixos/tests/apfs.nix @@ -0,0 +1,54 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "apfs"; + meta.maintainers = with pkgs.lib.maintainers; [ Luflosi ]; + + machine = { pkgs, ... }: { + virtualisation.emptyDiskImages = [ 1024 ]; + + boot.supportedFilesystems = [ "apfs" ]; + }; + + testScript = '' + machine.wait_for_unit("basic.target") + machine.succeed("mkdir /tmp/mnt") + + with subtest("mkapfs refuses to work with a label that is too long"): + machine.fail( "mkapfs -L '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F' /dev/vdb") + + with subtest("mkapfs works with the maximum label length"): + machine.succeed("mkapfs -L '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7' /dev/vdb") + + with subtest("Enable case sensitivity and normalization sensitivity"): + machine.succeed( + "mkapfs -s -z /dev/vdb", + # Triggers a bug, see https://github.com/linux-apfs/linux-apfs-rw/issues/15 + # "mount -o cknodes,readwrite /dev/vdb /tmp/mnt", + "mount -o readwrite /dev/vdb /tmp/mnt", + "echo 'Hello World 1' > /tmp/mnt/test.txt", + "[ ! -f /tmp/mnt/TeSt.TxT ] || false", # Test case sensitivity + "echo 'Hello World 1' | diff - /tmp/mnt/test.txt", + "echo 'Hello World 2' > /tmp/mnt/\u0061\u0301.txt", + "echo 'Hello World 2' | diff - /tmp/mnt/\u0061\u0301.txt", + "[ ! -f /tmp/mnt/\u00e1.txt ] || false", # Test Unicode normalization sensitivity + "umount /tmp/mnt", + "apfsck /dev/vdb", + ) + with subtest("Disable case sensitivity and normalization sensitivity"): + machine.succeed( + "mkapfs /dev/vdb", + "mount -o readwrite /dev/vdb /tmp/mnt", + "echo 'bla bla bla' > /tmp/mnt/Test.txt", + "echo -n 'Hello World' > /tmp/mnt/test.txt", + "echo ' 1' >> /tmp/mnt/TEST.TXT", + "umount /tmp/mnt", + "apfsck /dev/vdb", + "mount -o readwrite /dev/vdb /tmp/mnt", + "echo 'Hello World 1' | diff - /tmp/mnt/TeSt.TxT", # Test case insensitivity + "echo 'Hello World 2' > /tmp/mnt/\u0061\u0301.txt", + "echo 'Hello World 2' | diff - /tmp/mnt/\u0061\u0301.txt", + "echo 'Hello World 2' | diff - /tmp/mnt/\u00e1.txt", # Test Unicode normalization + "umount /tmp/mnt", + "apfsck /dev/vdb", + ) + ''; +})