Unverified Commit d24ca470 authored by Ramses's avatar Ramses Committed by GitHub
Browse files

nixos/etc-overlay: make the etc overlay compatible with nixos-enter and nixos-install (#364239)

parents 108e6566 df7c405f
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
      system.activationScripts.etc = lib.stringAfter [
        "users"
        "groups"
        "specialfs"
      ] config.system.build.etcActivationCommands;
    }

@@ -48,7 +49,7 @@
      boot.initrd.systemd = {
        mounts = [
          {
            where = "/run/etc-metadata";
            where = "/run/nixos-etc-metadata";
            what = "/etc-metadata-image";
            type = "erofs";
            options = "loop,ro";
@@ -83,7 +84,7 @@
                "relatime"
                "redirect_dir=on"
                "metacopy=on"
                "lowerdir=/run/etc-metadata::/etc-basedir"
                "lowerdir=/run/nixos-etc-metadata::/etc-basedir"
              ]
              ++ lib.optionals config.system.etc.overlay.mutable [
                "rw"
@@ -113,7 +114,7 @@
            unitConfig = {
              RequiresMountsFor = [
                "/sysroot/nix/store"
                "/run/etc-metadata"
                "/run/nixos-etc-metadata"
              ];
              DefaultDependencies = false;
            };
+78 −56
Original line number Diff line number Diff line
@@ -250,23 +250,44 @@ in
        );
      in
      if config.system.etc.overlay.enable then
        #bash
        ''
          # This script atomically remounts /etc when switching configuration. On a (re-)boot
          # this should not run because /etc is mounted via a systemd mount unit
          # instead. To a large extent this mimics what composefs does. Because
          # This script atomically remounts /etc when switching configuration.
          # On a (re-)boot this should not run because /etc is mounted via a
          # systemd mount unit instead.
          # The activation script can also be called in cases where we didn't have
          # an initrd though, like for instance when using  nixos-enter,
          # so we cannot assume that /etc has already been mounted.
          #
          # To a large extent this mimics what composefs does. Because
          # it's relatively simple, however, we avoid the composefs dependency.
          # Since this script is not idempotent, it should not run when etc hasn't
          # changed.
          if [[ ! $IN_NIXOS_SYSTEMD_STAGE1 ]] && [[ "${config.system.build.etc}/etc" != "$(readlink -f /run/current-system/etc)" ]]; then
            echo "remounting /etc..."

            tmpMetadataMount=$(mktemp --directory -t nixos-etc-metadata.XXXXXXXXXX)
            ${lib.optionalString config.system.etc.overlay.mutable ''
              # These directories are usually created in initrd,
              # but we need to create them here when we didn't we're called directly,
              # for instance by nixos-enter
              mkdir --parents /.rw-etc/upper /.rw-etc/work
              chmod --recursive 0755 /.rw-etc
            ''}

            tmpMetadataMount=$(TMPDIR="/run" mktemp --directory -t nixos-etc-metadata.XXXXXXXXXX)
            mount --type erofs -o ro ${config.system.build.etcMetadataImage} $tmpMetadataMount

            # There was no previous /etc mounted. This happens when we're called
            # directly without an initrd, like with nixos-enter.
            if ! mountpoint -q /etc; then
              mount --type overlay overlay \
                --options lowerdir=$tmpMetadataMount::${config.system.build.etcBasedir},${etcOverlayOptions} \
                /etc
            else
              # Mount the new /etc overlay to a temporary private mount.
              # This needs the indirection via a private bind mount because you
              # cannot move shared mounts.
            tmpEtcMount=$(mktemp --directory -t nixos-etc.XXXXXXXXXX)
              tmpEtcMount=$(TMPDIR="/run" mktemp --directory -t nixos-etc.XXXXXXXXXX)
              mount --bind --make-private $tmpEtcMount $tmpEtcMount
              mount --type overlay overlay \
                --options lowerdir=$tmpMetadataMount::${config.system.build.etcBasedir},${etcOverlayOptions} \
@@ -313,15 +334,16 @@ in
              # Unmount the temporary mount
              umount --lazy "$tmpEtcMount"
              rmdir "$tmpEtcMount"
            fi

            # Unmount old metadata mounts
            # For some reason, `findmnt /tmp --submounts` does not show the nested
            # mounts. So we'll just find all mounts of type erofs and filter on the
            # name of the mountpoint.
            findmnt --type erofs --list --kernel --output TARGET | while read -r mountPoint; do
              if [[ "$mountPoint" =~ ^/tmp/nixos-etc-metadata\..{10}$ &&
              if [[ ("$mountPoint" =~ ^/run/nixos-etc-metadata\..{10}$ || "$mountPoint" =~ ^/run/nixos-etc-metadata$ ) &&
                    "$mountPoint" != "$tmpMetadataMount" ]]; then
                umount --lazy $mountPoint
                umount --lazy "$mountPoint"
                rmdir "$mountPoint"
              fi
            done
+10 −6
Original line number Diff line number Diff line
@@ -39,8 +39,8 @@
    ''
      newergen = machine.succeed("realpath /run/current-system/specialisation/newer-generation/bin/switch-to-configuration").rstrip()

      with subtest("/run/etc-metadata/ is mounted"):
        print(machine.succeed("mountpoint /run/etc-metadata"))
      with subtest("/run/nixos-etc-metadata/ is mounted"):
        print(machine.succeed("mountpoint /run/nixos-etc-metadata"))

      with subtest("No temporary files leaked into stage 2"):
        machine.succeed("[ ! -e /etc-metadata-image ]")
@@ -91,10 +91,14 @@

        machine.succeed(f"{newergen} switch")

        tmpMounts = machine.succeed("find /tmp -maxdepth 1 -type d -regex '/tmp/nixos-etc\\..*' | wc -l").rstrip()
        metaMounts = machine.succeed("find /tmp -maxdepth 1 -type d -regex '/tmp/nixos-etc-metadata\\..*' | wc -l").rstrip()
        tmpMounts = machine.succeed("find /run -maxdepth 1 -type d -regex '/run/nixos-etc\\..*'").rstrip()
        print(tmpMounts)
        metaMounts = machine.succeed("find /run -maxdepth 1 -type d -regex '/run/nixos-etc-metadata.*'").rstrip()
        print(metaMounts)

        assert tmpMounts == "0", f"Found {tmpMounts} remaining tmpmounts"
        assert metaMounts == "1", f"Found {metaMounts} remaining metamounts"
        numOfTmpMounts = len(tmpMounts.splitlines())
        numOfMetaMounts = len(metaMounts.splitlines())
        assert numOfTmpMounts == 0, f"Found {numOfTmpMounts} remaining tmpmounts"
        assert numOfMetaMounts == 1, f"Found {numOfMetaMounts} remaining metamounts"
    '';
}
+10 −6
Original line number Diff line number Diff line
@@ -27,8 +27,8 @@
    ''
      newergen = machine.succeed("realpath /run/current-system/specialisation/newer-generation/bin/switch-to-configuration").rstrip()

      with subtest("/run/etc-metadata/ is mounted"):
        print(machine.succeed("mountpoint /run/etc-metadata"))
      with subtest("/run/nixos-etc-metadata/ is mounted"):
        print(machine.succeed("mountpoint /run/nixos-etc-metadata"))

      with subtest("No temporary files leaked into stage 2"):
        machine.succeed("[ ! -e /etc-metadata-image ]")
@@ -68,10 +68,14 @@
        machine.succeed(f"{newergen} switch")
        assert machine.succeed("cat /etc/newergen") == "newergen"

        tmpMounts = machine.succeed("find /tmp -maxdepth 1 -type d -regex '/tmp/nixos-etc\\..*' | wc -l").rstrip()
        metaMounts = machine.succeed("find /tmp -maxdepth 1 -type d -regex '/tmp/nixos-etc-metadata\\..*' | wc -l").rstrip()
        tmpMounts = machine.succeed("find /run -maxdepth 1 -type d -regex '/run/nixos-etc\\..*'").rstrip()
        print(tmpMounts)
        metaMounts = machine.succeed("find /run -maxdepth 1 -type d -regex '/run/nixos-etc-metadata.*'").rstrip()
        print(metaMounts)

        assert tmpMounts == "0", f"Found {tmpMounts} remaining tmpmounts"
        assert metaMounts == "1", f"Found {metaMounts} remaining metamounts"
        numOfTmpMounts = len(tmpMounts.splitlines())
        numOfMetaMounts = len(metaMounts.splitlines())
        assert numOfTmpMounts == 0, f"Found {numOfTmpMounts} remaining tmpmounts"
        assert numOfMetaMounts == 1, f"Found {numOfMetaMounts} remaining metamounts"
    '';
}
+3 −2
Original line number Diff line number Diff line
@@ -58,10 +58,11 @@ if [[ ! -e $mountPoint/etc/NIXOS ]]; then
    exit 126
fi

mkdir -p "$mountPoint/dev" "$mountPoint/sys"
chmod 0755 "$mountPoint/dev" "$mountPoint/sys"
mkdir -p "$mountPoint/dev" "$mountPoint/sys" "$mountPoint/proc"
chmod 0755 "$mountPoint/dev" "$mountPoint/sys" "$mountPoint/proc"
mount --rbind /dev "$mountPoint/dev"
mount --rbind /sys "$mountPoint/sys"
mount --rbind /proc "$mountPoint/proc"

# modified from https://github.com/archlinux/arch-install-scripts/blob/bb04ab435a5a89cd5e5ee821783477bc80db797f/arch-chroot.in#L26-L52
chroot_add_resolv_conf() {