···6969chown -f 0:30000 /nix/store
7070chmod -f 1775 /nix/store
7171if [ -n "@readOnlyNixStore@" ]; then
7272- if ! [[ "$(findmnt --noheadings --output OPTIONS /nix/store)" =~ ro(,|$) ]]; then
7272+ # #375257: Ensure that we pick the "top" (i.e. last) mount so we don't get a false positive for a lower mount.
7373+ if ! [[ "$(findmnt --direction backward --first-only --noheadings --output OPTIONS /nix/store)" =~ (^|,)ro(,|$) ]]; then
7374 if [ -z "$container" ]; then
7475 mount --bind /nix/store /nix/store
7576 else
···11+import ./make-test-python.nix (
22+ { pkgs, ... }:
33+ {
44+ name = "boot-stage2";
55+66+ nodes.machine =
77+ {
88+ config,
99+ pkgs,
1010+ lib,
1111+ ...
1212+ }:
1313+ {
1414+ virtualisation = {
1515+ emptyDiskImages = [ 256 ];
1616+1717+ # Mount an ext4 as the upper layer of the Nix store.
1818+ fileSystems = {
1919+ "/nix/store" = lib.mkForce {
2020+ device = "/dev/vdb"; # the above disk image
2121+ fsType = "ext4";
2222+2323+ # data=journal always displays after errors=remount-ro; this is only needed because of the overlay
2424+ # and #375257 will trigger with `errors=remount-ro` on a non-overlaid store:
2525+ # see ordering in https://github.com/torvalds/linux/blob/v6.12/fs/ext4/super.c#L2974
2626+ options = [
2727+ "defaults"
2828+ "errors=remount-ro"
2929+ "data=journal"
3030+ ];
3131+ };
3232+ };
3333+ };
3434+3535+ boot = {
3636+ initrd = {
3737+ # Format the upper Nix store.
3838+ postDeviceCommands = ''
3939+ ${pkgs.e2fsprogs}/bin/mkfs.ext4 /dev/vdb
4040+ '';
4141+4242+ # Overlay the RO store onto it.
4343+ # Note that bug #375257 can be triggered without an overlay,
4444+ # using the errors=remount-ro option (or similar) or with an overlay where any of the
4545+ # paths ends in 'ro'. The offending mountpoint also has to be the last (top) one
4646+ # if an option ending in 'ro' is the last in the list, so test both cases here.
4747+ postMountCommands = ''
4848+ mkdir -p /mnt-root/nix/store/ro /mnt-root/nix/store/rw /mnt-root/nix/store/work
4949+ mount --bind /mnt-root/nix/.ro-store /mnt-root/nix/store/ro
5050+ mount -t overlay overlay \
5151+ -o lowerdir=/mnt-root/nix/store/ro,upperdir=/mnt-root/nix/store/rw,workdir=/mnt-root/nix/store/work \
5252+ /mnt-root/nix/store
5353+ '';
5454+5555+ kernelModules = [ "overlay" ];
5656+ };
5757+5858+ postBootCommands = ''
5959+ touch /etc/post-boot-ran
6060+ mount
6161+ '';
6262+ };
6363+ };
6464+6565+ testScript = ''
6666+ machine.wait_for_unit("multi-user.target")
6767+ machine.succeed("test /etc/post-boot-ran")
6868+ machine.fail("touch /nix/store/should-not-work");
6969+ '';
7070+7171+ meta.maintainers = with pkgs.lib.maintainers; [ numinit ];
7272+ }
7373+)