diff --git a/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/mount.conf b/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/mount.conf index dad6c229df2b..7113acc881a9 100644 --- a/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/mount.conf +++ b/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/mount.conf @@ -20,8 +20,20 @@ extraMounts: mountPoint: /sys/firmware/efi/efivars efi: true -# Ensure the right fmask/dmask is set on the ESP, as it will be -# picked up by nixos-generate-config later +# Btrfs subvolume layout for NixOS +# Empty subvolume for / means no subvolume (top-level) for GRUB compatibility +btrfsSubvolumes: + - mountPoint: / + subvolume: "" + - mountPoint: /home + subvolume: /home + - mountPoint: /nix + subvolume: /nix + +# Mount options by filesystem type +# nixos-generate-config picks these up for hardware-configuration.nix mountOptions: - filesystem: efi options: [ fmask=0077, dmask=0077 ] + - filesystem: btrfs + options: [ compress=zstd, noatime ] diff --git a/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/partition.conf b/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/partition.conf index 3e067e8d2343..75d04cec9dfa 100644 --- a/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/partition.conf +++ b/pkgs/by-name/ca/calamares-nixos-extensions/src/config/modules/partition.conf @@ -12,11 +12,29 @@ userSwapChoices: luksGeneration: luks2 +defaultFileSystemType: "ext4" + +availableFileSystemTypes: + - ext4 + - btrfs + - xfs + - f2fs + showNotEncryptedBootMessage: false +bios: + mountPoint: "/boot" + minimumSize: 512MiB + recommendedSize: 1GiB + label: "BOOT" + partitionLayout: - - name: "root" + - name: "boot" filesystem: "ext4" + mountPoint: "/boot" + size: 1GiB + - name: "root" + filesystem: "unknown" noEncrypt: false mountPoint: "/" size: 100% diff --git a/pkgs/by-name/ca/calamares-nixos-extensions/src/modules/nixos/main.py b/pkgs/by-name/ca/calamares-nixos-extensions/src/modules/nixos/main.py index 5987724728f8..e9bee7a245d4 100644 --- a/pkgs/by-name/ca/calamares-nixos-extensions/src/modules/nixos/main.py +++ b/pkgs/by-name/ca/calamares-nixos-extensions/src/modules/nixos/main.py @@ -45,6 +45,15 @@ cfgbootbios = """ # Bootloader. """ +cfgbootbiosbtrfs = """ # Bootloader. + boot.loader.grub.enable = true; + boot.loader.grub.device = "@@bootdev@@"; + boot.loader.grub.useOSProber = true; + # Use provided UUIDs instead of blkid probing (required for btrfs subvolumes) + boot.loader.grub.fsIdentifier = "provided"; + +""" + cfgbootnone = """ # Disable bootloader. boot.loader.grub.enable = false; @@ -360,6 +369,46 @@ def catenate(d, key, *values): d[key] = "".join(values) +def fix_btrfs_subvolumes(hardware_config, partitions): + """ + Fix btrfs subvolume configuration in hardware-configuration.nix. + + nixos-generate-config generates incorrect subvol options for btrfs + by setting them to mount points instead of actual subvolume names. + This function corrects the subvol options to match what Calamares configured. + """ + # Map of mount points to their correct btrfs subvolume names + # These match what is configured in mount.conf btrfsSubvolumes + # Root uses top-level (no subvolume) for GRUB compatibility + subvol_map = { + "/home": "home", + "/nix": "nix", + } + + # Check if root is actually btrfs + root_is_btrfs = False + for part in partitions: + if part.get("mountPoint") == "/" and part.get("fs") == "btrfs": + root_is_btrfs = True + break + + if not root_is_btrfs: + return hardware_config + + libcalamares.utils.debug("Fixing btrfs subvolume configuration") + + # Fix subvol options for each mount point + # nixos-generate-config may generate "subvol=/" instead of "subvol=@" + for mount_point, correct_subvol in subvol_map.items(): + # Match any "subvol=" and replace with correct subvolume + # This handles cases like "subvol=/" or "subvol=/home" + pattern = r'(fileSystems\."{}"[^;]*"subvol=)[^"]*"'.format(re.escape(mount_point)) + replacement = r'\g<1>{}"'.format(correct_subvol) + hardware_config = re.sub(pattern, replacement, hardware_config, flags=re.DOTALL) + + return hardware_config + + def run(): """NixOS Configuration.""" @@ -388,11 +437,22 @@ def run(): # Pick config parts and prepare substitution + # Check if root filesystem is btrfs + root_is_btrfs = False + for part in gs.value("partitions"): + if part.get("mountPoint") == "/" and part.get("fs") == "btrfs": + root_is_btrfs = True + break + # Check bootloader if fw_type == "efi": cfg += cfgbootefi elif bootdev != "nodev": - cfg += cfgbootbios + # Use btrfs-specific config to avoid blkid issues with subvolumes + if root_is_btrfs: + cfg += cfgbootbiosbtrfs + else: + cfg += cfgbootbios catenate(variables, "bootdev", bootdev) else: cfg += cfgbootnone @@ -734,9 +794,20 @@ def run(): libcalamares.utils.error(e.output.decode("utf8")) return (_("nixos-generate-config failed"), _(e.output.decode("utf8"))) - # Check for unfree stuff in hardware-configuration.nix + # Read and fix hardware-configuration.nix hf = open(root_mount_point + "/etc/nixos/hardware-configuration.nix", "r") htxt = hf.read() + hf.close() + + hardware_modified = False + + # Fix btrfs subvolume configuration if needed + htxt_fixed = fix_btrfs_subvolumes(htxt, gs.value("partitions")) + if htxt_fixed != htxt: + htxt = htxt_fixed + hardware_modified = True + + # Check for unfree stuff in hardware-configuration.nix search = re.search(r"boot\.extraModulePackages = \[ (.*) \];", htxt) # Check if any extraModulePackages are defined, and remove if only free packages are allowed @@ -765,14 +836,17 @@ def run(): ) ) expkgs.remove(pkg) - hardwareout = re.sub( + htxt = re.sub( r"boot\.extraModulePackages = \[ (.*) \];", "boot.extraModulePackages = [ {}];".format( "".join(map(lambda x: x + " ", expkgs)) ), htxt, ) - # Write the hardware-configuration.nix file + hardware_modified = True + + # Write the hardware-configuration.nix file if modified + if hardware_modified: libcalamares.utils.host_env_process_output( [ "cp", @@ -780,7 +854,7 @@ def run(): root_mount_point + "/etc/nixos/hardware-configuration.nix", ], None, - hardwareout, + htxt, ) # Write the configuration.nix file @@ -789,6 +863,14 @@ def run(): status = _("Installing NixOS") libcalamares.job.setprogress(0.3) + try: + subprocess.check_output( + ["pkexec", "chmod", "755", root_mount_point], + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + libcalamares.utils.warning("Failed to set permissions on {}: {}".format(root_mount_point, e.output)) + # build nixos-install command nixosInstallCmd = [ "pkexec" ] nixosInstallCmd.extend(generateProxyStrings())