nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix

nixos/image/repart: allow replacing `/nix/store`

+152 -85
+46 -4
nixos/doc/manual/installation/building-images-via-systemd-repart.chapter.md
··· 40 40 } 41 41 ``` 42 42 43 - ## Nix Store Partition {#sec-image-repart-store-partition} 43 + ## Nix Store Paths {#sec-image-repart-store-paths} 44 + 45 + If you want to rewrite Nix store paths, e.g., to remove the `/nix/store` prefix 46 + or to nest it below a parent path, you can do that through the 47 + `nixStorePrefix` option. 48 + 49 + ### Nix Store Partition {#sec-image-repart-store-partition} 44 50 45 51 You can define a partition that only contains the Nix store and then mount it 46 52 under `/nix/store`. Because the `/nix/store` part of the paths is already 47 - determined by the mount point, you have to set `stripNixStorePrefix = true;` so 48 - that the prefix is stripped from the paths before copying them into the image. 53 + determined by the mount point, you have to set `nixStorePrefix = "/"` so 54 + that `/nix/store` is stripped from the paths before copying them into the image. 49 55 50 56 ```nix 51 57 { ··· 60 54 image.repart.partitions = { 61 55 "store" = { 62 56 storePaths = [ config.system.build.toplevel ]; 63 - stripNixStorePrefix = true; 57 + nixStorePrefix = "/"; 64 58 repartConfig = { 65 59 Type = "linux-generic"; 66 60 Label = "nix-store"; 61 + # ... 62 + }; 63 + }; 64 + }; 65 + } 66 + ``` 67 + 68 + ### Nix Store Subvolume {#sec-image-repart-store-subvolume} 69 + 70 + Alternatively, you can create a Btrfs subvolume `/@nix-store` containing the 71 + Nix store and mount it on `/nix/store`: 72 + 73 + ```nix 74 + { 75 + fileSystems."/" = { 76 + device = "/dev/disk/by-partlabel/root"; 77 + fsType = "btrfs"; 78 + options = [ "subvol=/@" ]; 79 + }; 80 + 81 + fileSystems."/nix/store" = { 82 + device = "/dev/disk/by-partlabel/root"; 83 + fsType = "btrfs"; 84 + options = [ "subvol=/@nix-store" ]; 85 + }; 86 + 87 + image.repart.partitions = { 88 + "root" = { 89 + storePaths = [ config.system.build.toplevel ]; 90 + nixStorePrefix = "/@nix-store"; 91 + repartConfig = { 92 + Type = "root"; 93 + Label = "root"; 94 + Format = "btrfs"; 95 + Subvolumes = "/@ /@nix-store"; 96 + MakeDirectories = "/@ /@nix-store"; 67 97 # ... 68 98 }; 69 99 };
+6
nixos/doc/manual/redirects.json
··· 287 287 "sec-image-repart": [ 288 288 "index.html#sec-image-repart" 289 289 ], 290 + "sec-image-repart-store-paths": [ 291 + "index.html#sec-image-repart-store-paths" 292 + ], 290 293 "sec-image-repart-store-partition": [ 291 294 "index.html#sec-image-repart-store-partition" 295 + ], 296 + "sec-image-repart-store-subvolume": [ 297 + "index.html#sec-image-repart-store-subvolume" 292 298 ], 293 299 "sec-image-repart-appliance": [ 294 300 "index.html#sec-image-repart-appliance"
+9 -7
nixos/modules/image/amend-repart-definitions.py
··· 36 36 37 37 38 38 def add_closure_to_definition( 39 - definition: Path, closure: Path | None, strip_nix_store_prefix: bool | None 39 + definition: Path, closure: Path | None, nix_store_prefix: str | None 40 40 ) -> None: 41 41 """Add CopyFiles= instructions to a definition for all paths in the closure. 42 42 43 - If strip_nix_store_prefix is True, `/nix/store` is stripped from the target path. 43 + Replace `/nix/store` with the value of nix_store_prefix. 44 44 """ 45 45 if not closure: 46 46 return ··· 52 52 continue 53 53 54 54 source = Path(line.strip()) 55 - target = str(source.relative_to("/nix/store/")) 56 - target = f":/{target}" if strip_nix_store_prefix else "" 55 + option = f"CopyFiles={source}" 56 + if nix_store_prefix: 57 + target = nix_store_prefix / source.relative_to("/nix/store/") 58 + option = f"{option}:{target}" 57 59 58 - copy_files_lines.append(f"CopyFiles={source}{target}\n") 60 + copy_files_lines.append(f"{option}\n") 59 61 60 62 with open(definition, "a") as f: 61 63 f.writelines(copy_files_lines) ··· 104 102 add_contents_to_definition(definition, contents) 105 103 106 104 closure = config.get("closure") 107 - strip_nix_store_prefix = config.get("stripNixStorePrefix") 108 - add_closure_to_definition(definition, closure, strip_nix_store_prefix) 105 + nix_store_prefix = config.get("nixStorePrefix") 106 + add_closure_to_definition(definition, closure, nix_store_prefix) 109 107 110 108 print(target_dir.absolute()) 111 109
+91 -74
nixos/modules/image/repart.nix
··· 15 15 16 16 inherit (utils.systemdUtils.lib) GPTMaxLabelLength; 17 17 18 - partitionOptions = { 19 - options = { 20 - storePaths = lib.mkOption { 21 - type = with lib.types; listOf path; 22 - default = [ ]; 23 - description = "The store paths to include in the partition."; 24 - }; 25 - 26 - stripNixStorePrefix = lib.mkOption { 27 - type = lib.types.bool; 28 - default = false; 29 - description = '' 30 - Whether to strip `/nix/store/` from the store paths. This is useful 31 - when you want to build a partition that only contains store paths and 32 - is mounted under `/nix/store`. 33 - ''; 34 - }; 35 - 36 - contents = lib.mkOption { 37 - type = 38 - with lib.types; 39 - attrsOf (submodule { 40 - options = { 41 - source = lib.mkOption { 42 - type = types.path; 43 - description = "Path of the source file."; 44 - }; 45 - }; 46 - }); 47 - default = { }; 48 - example = lib.literalExpression '' 49 - { 50 - "/EFI/BOOT/BOOTX64.EFI".source = 51 - "''${pkgs.systemd}/lib/systemd/boot/efi/systemd-bootx64.efi"; 52 - 53 - "/loader/entries/nixos.conf".source = systemdBootEntry; 54 - } 55 - ''; 56 - description = "The contents to end up in the filesystem image."; 57 - }; 58 - 59 - repartConfig = lib.mkOption { 60 - type = 61 - with lib.types; 62 - attrsOf (oneOf [ 63 - str 64 - int 65 - bool 66 - (listOf str) 67 - ]); 68 - example = { 69 - Type = "home"; 70 - SizeMinBytes = "512M"; 71 - SizeMaxBytes = "2G"; 18 + partitionOptions = 19 + { config, ... }: 20 + { 21 + options = { 22 + storePaths = lib.mkOption { 23 + type = with lib.types; listOf path; 24 + default = [ ]; 25 + description = "The store paths to include in the partition."; 72 26 }; 73 - description = '' 74 - Specify the repart options for a partiton as a structural setting. 75 - See {manpage}`repart.d(5)` 76 - for all available options. 77 - ''; 27 + 28 + # Superseded by `nixStorePrefix`. Unfortunately, `mkChangedOptionModule` 29 + # does not support submodules. 30 + stripNixStorePrefix = lib.mkOption { 31 + default = "_mkMergedOptionModule"; 32 + visible = false; 33 + }; 34 + 35 + nixStorePrefix = lib.mkOption { 36 + type = lib.types.path; 37 + default = "/nix/store"; 38 + description = '' 39 + The prefix to use for store paths. Defaults to `/nix/store`. This is 40 + useful when you want to build a partition that only contains store 41 + paths and is mounted under `/nix/store` or if you want to create the 42 + store paths below a parent path (e.g., `/@nix/nix/store`). 43 + ''; 44 + }; 45 + 46 + contents = lib.mkOption { 47 + type = 48 + with lib.types; 49 + attrsOf (submodule { 50 + options = { 51 + source = lib.mkOption { 52 + type = types.path; 53 + description = "Path of the source file."; 54 + }; 55 + }; 56 + }); 57 + default = { }; 58 + example = lib.literalExpression '' 59 + { 60 + "/EFI/BOOT/BOOTX64.EFI".source = 61 + "''${pkgs.systemd}/lib/systemd/boot/efi/systemd-bootx64.efi"; 62 + 63 + "/loader/entries/nixos.conf".source = systemdBootEntry; 64 + } 65 + ''; 66 + description = "The contents to end up in the filesystem image."; 67 + }; 68 + 69 + repartConfig = lib.mkOption { 70 + type = 71 + with lib.types; 72 + attrsOf (oneOf [ 73 + str 74 + int 75 + bool 76 + (listOf str) 77 + ]); 78 + example = { 79 + Type = "home"; 80 + SizeMinBytes = "512M"; 81 + SizeMaxBytes = "2G"; 82 + }; 83 + description = '' 84 + Specify the repart options for a partiton as a structural setting. 85 + See {manpage}`repart.d(5)` 86 + for all available options. 87 + ''; 88 + }; 89 + }; 90 + 91 + config = lib.mkIf (config.stripNixStorePrefix == true) { 92 + nixStorePrefix = "/"; 78 93 }; 79 94 }; 80 - }; 81 95 82 96 mkfsOptionsToEnv = 83 97 opts: ··· 364 350 } 365 351 ) cfg.partitions; 366 352 367 - warnings = lib.filter (v: v != null) ( 353 + warnings = lib.flatten ( 368 354 lib.mapAttrsToList ( 369 355 fileName: partitionConfig: 370 356 let ··· 372 358 suggestedMaxLabelLength = GPTMaxLabelLength - 2; 373 359 labelLength = builtins.stringLength repartConfig.Label; 374 360 in 375 - if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then 376 - '' 377 - The partition label '${repartConfig.Label}' 378 - defined for '${fileName}' is ${toString labelLength} characters long. 379 - The suggested maximum label length is ${toString suggestedMaxLabelLength}. 361 + lib.optional (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) '' 362 + The partition label '${repartConfig.Label}' 363 + defined for '${fileName}' is ${toString labelLength} characters long. 364 + The suggested maximum label length is ${toString suggestedMaxLabelLength}. 380 365 381 - If you use sytemd-sysupdate style A/B updates, this might 382 - not leave enough space to increment the version number included in 383 - the label in a future release. For example, if your label is 384 - ${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and 385 - you're at version 9, you cannot increment this to 10. 386 - '' 387 - else 388 - null 366 + If you use sytemd-sysupdate style A/B updates, this might 367 + not leave enough space to increment the version number included in 368 + the label in a future release. For example, if your label is 369 + ${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and 370 + you're at version 9, you cannot increment this to 10. 371 + '' 372 + ++ lib.optional (partitionConfig.stripNixStorePrefix != "_mkMergedOptionModule") '' 373 + The option definition `image.repart.paritions.${fileName}.stripNixStorePrefix` 374 + has changed to `image.repart.paritions.${fileName}.nixStorePrefix` and now 375 + accepts the path to use as prefix directly. Use `nixStorePrefix = "/"` to 376 + achieve the same effect as setting `stripNixStorePrefix = true`. 377 + '' 389 378 ) cfg.partitions 390 379 ); 391 380 };