···69chown -f 0:30000 /nix/store
70chmod -f 1775 /nix/store
71if [ -n "@readOnlyNixStore@" ]; then
72- if ! [[ "$(findmnt --noheadings --output OPTIONS /nix/store)" =~ ro(,|$) ]]; then
073 if [ -z "$container" ]; then
74 mount --bind /nix/store /nix/store
75 else
···69chown -f 0:30000 /nix/store
70chmod -f 1775 /nix/store
71if [ -n "@readOnlyNixStore@" ]; then
72+ # #375257: Ensure that we pick the "top" (i.e. last) mount so we don't get a false positive for a lower mount.
73+ if ! [[ "$(findmnt --direction backward --first-only --noheadings --output OPTIONS /nix/store)" =~ (^|,)ro(,|$) ]]; then
74 if [ -z "$container" ]; then
75 mount --bind /nix/store /nix/store
76 else
···1+import ./make-test-python.nix (
2+ { pkgs, ... }:
3+ {
4+ name = "boot-stage2";
5+6+ nodes.machine =
7+ {
8+ config,
9+ pkgs,
10+ lib,
11+ ...
12+ }:
13+ {
14+ virtualisation = {
15+ emptyDiskImages = [ 256 ];
16+17+ # Mount an ext4 as the upper layer of the Nix store.
18+ fileSystems = {
19+ "/nix/store" = lib.mkForce {
20+ device = "/dev/vdb"; # the above disk image
21+ fsType = "ext4";
22+23+ # data=journal always displays after errors=remount-ro; this is only needed because of the overlay
24+ # and #375257 will trigger with `errors=remount-ro` on a non-overlaid store:
25+ # see ordering in https://github.com/torvalds/linux/blob/v6.12/fs/ext4/super.c#L2974
26+ options = [
27+ "defaults"
28+ "errors=remount-ro"
29+ "data=journal"
30+ ];
31+ };
32+ };
33+ };
34+35+ boot = {
36+ initrd = {
37+ # Format the upper Nix store.
38+ postDeviceCommands = ''
39+ ${pkgs.e2fsprogs}/bin/mkfs.ext4 /dev/vdb
40+ '';
41+42+ # Overlay the RO store onto it.
43+ # Note that bug #375257 can be triggered without an overlay,
44+ # using the errors=remount-ro option (or similar) or with an overlay where any of the
45+ # paths ends in 'ro'. The offending mountpoint also has to be the last (top) one
46+ # if an option ending in 'ro' is the last in the list, so test both cases here.
47+ postMountCommands = ''
48+ mkdir -p /mnt-root/nix/store/ro /mnt-root/nix/store/rw /mnt-root/nix/store/work
49+ mount --bind /mnt-root/nix/.ro-store /mnt-root/nix/store/ro
50+ mount -t overlay overlay \
51+ -o lowerdir=/mnt-root/nix/store/ro,upperdir=/mnt-root/nix/store/rw,workdir=/mnt-root/nix/store/work \
52+ /mnt-root/nix/store
53+ '';
54+55+ kernelModules = [ "overlay" ];
56+ };
57+58+ postBootCommands = ''
59+ touch /etc/post-boot-ran
60+ mount
61+ '';
62+ };
63+ };
64+65+ testScript = ''
66+ machine.wait_for_unit("multi-user.target")
67+ machine.succeed("test /etc/post-boot-ran")
68+ machine.fail("touch /nix/store/should-not-work");
69+ '';
70+71+ meta.maintainers = with pkgs.lib.maintainers; [ numinit ];
72+ }
73+)