at master 7.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8 9let 10 cfg = config.systemd.repart; 11 initrdCfg = config.boot.initrd.systemd.repart; 12 13 format = pkgs.formats.ini { listsAsDuplicateKeys = true; }; 14 15 definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format ( 16 lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions 17 ); 18 19 partitionAssertions = lib.mapAttrsToList ( 20 fileName: definition: 21 let 22 inherit (utils.systemdUtils.lib) GPTMaxLabelLength; 23 labelLength = builtins.stringLength definition.Label; 24 in 25 { 26 assertion = definition ? Label -> GPTMaxLabelLength >= labelLength; 27 message = '' 28 The partition label '${definition.Label}' defined for '${fileName}' is ${toString labelLength} 29 characters long, but the maximum label length supported by systemd is ${toString GPTMaxLabelLength}. 30 ''; 31 } 32 ) cfg.partitions; 33in 34{ 35 options = { 36 boot.initrd.systemd.repart = { 37 enable = lib.mkEnableOption "systemd-repart" // { 38 description = '' 39 Grow and add partitions to a partition table at boot time in the initrd. 40 systemd-repart only works with GPT partition tables. 41 42 To run systemd-repart after the initrd, see 43 `options.systemd.repart.enable`. 44 ''; 45 }; 46 47 device = lib.mkOption { 48 type = with lib.types; nullOr str; 49 description = '' 50 The device to operate on. 51 52 If `device == null`, systemd-repart will operate on the device 53 backing the root partition. So in order to dynamically *create* the 54 root partition in the initrd you need to set a device. 55 ''; 56 default = null; 57 example = "/dev/vda"; 58 }; 59 60 empty = lib.mkOption { 61 type = lib.types.enum [ 62 "refuse" 63 "allow" 64 "require" 65 "force" 66 "create" 67 ]; 68 description = '' 69 Controls how to operate on empty devices that contain no partition table yet. 70 See {manpage}`systemd-repart(8)` for details. 71 ''; 72 example = "require"; 73 default = "refuse"; 74 }; 75 76 discard = lib.mkOption { 77 type = lib.types.bool; 78 description = '' 79 Controls whether to issue the BLKDISCARD I/O control command on the 80 space taken up by any added partitions or on the space in between them. 81 Usually, it's a good idea to issue this request since it tells the underlying 82 hardware that the covered blocks shall be considered empty, improving performance. 83 84 See {manpage}`systemd-repart(8)` for details. 85 ''; 86 default = true; 87 }; 88 89 extraArgs = lib.mkOption { 90 description = '' 91 Extra command-line arguments to pass to systemd-repart. 92 93 See {manpage}`systemd-repart(8)` for all available options. 94 ''; 95 type = lib.types.listOf lib.types.str; 96 default = [ ]; 97 }; 98 }; 99 100 systemd.repart = { 101 enable = lib.mkEnableOption "systemd-repart" // { 102 description = '' 103 Grow and add partitions to a partition table. 104 systemd-repart only works with GPT partition tables. 105 106 To run systemd-repart while in the initrd, see 107 `options.boot.initrd.systemd.repart.enable`. 108 ''; 109 }; 110 111 partitions = lib.mkOption { 112 type = 113 with lib.types; 114 attrsOf ( 115 attrsOf (oneOf [ 116 str 117 int 118 bool 119 (listOf str) 120 ]) 121 ); 122 default = { }; 123 example = { 124 "10-root" = { 125 Type = "root"; 126 }; 127 "20-home" = { 128 Type = "home"; 129 SizeMinBytes = "512M"; 130 SizeMaxBytes = "2G"; 131 }; 132 }; 133 description = '' 134 Specify partitions as a set of the names of the definition files as the 135 key and the partition configuration as its value. The partition 136 configuration can use all upstream options. See {manpage}`repart.d(5)` 137 for all available options. 138 ''; 139 }; 140 }; 141 }; 142 143 config = lib.mkIf (cfg.enable || initrdCfg.enable) { 144 assertions = [ 145 { 146 assertion = initrdCfg.enable -> config.boot.initrd.systemd.enable; 147 message = '' 148 'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled. 149 ''; 150 } 151 ] 152 ++ partitionAssertions; 153 154 # systemd-repart uses loopback devices for partition creation 155 boot.initrd.availableKernelModules = lib.optional initrdCfg.enable "loop"; 156 157 boot.initrd.systemd = lib.mkIf initrdCfg.enable { 158 additionalUpstreamUnits = [ 159 "systemd-repart.service" 160 ]; 161 162 storePaths = [ 163 "${config.boot.initrd.systemd.package}/bin/systemd-repart" 164 ]; 165 166 contents."/etc/repart.d".source = definitionsDirectory; 167 168 # Override defaults in upstream unit. 169 services.systemd-repart = 170 let 171 deviceUnit = "${utils.escapeSystemdPath initrdCfg.device}.device"; 172 in 173 { 174 # systemd-repart tries to create directories in /var/tmp by default to 175 # store large temporary files that benefit from persistence on disk. In 176 # the initrd, however, /var/tmp does not provide more persistence than 177 # /tmp, so we re-use it here. 178 environment."TMPDIR" = "/tmp"; 179 serviceConfig = { 180 ExecStart = [ 181 " " # required to unset the previous value. 182 # When running in the initrd, systemd-repart by default searches 183 # for definition files in /sysroot or /sysusr. We tell it to look 184 # in the initrd itself. 185 '' 186 ${config.boot.initrd.systemd.package}/bin/systemd-repart \ 187 --definitions=/etc/repart.d \ 188 --dry-run=no \ 189 --empty=${initrdCfg.empty} \ 190 --discard=${lib.boolToString initrdCfg.discard} \ 191 ${utils.escapeSystemdExecArgs initrdCfg.extraArgs} \ 192 ${lib.optionalString (initrdCfg.device != null) initrdCfg.device} 193 '' 194 ]; 195 }; 196 # systemd-repart needs to run after /sysroot (or /sysuser, but we 197 # don't have it) has been mounted because otherwise it cannot 198 # determine the device (i.e disk) to operate on. If you want to run 199 # systemd-repart without /sysroot (i.e. to create the root 200 # partition), you have to explicitly tell it which device to operate 201 # on. The service then needs to be ordered to run after this device 202 # is available. 203 requires = lib.mkIf (initrdCfg.device != null) [ deviceUnit ]; 204 after = if initrdCfg.device == null then [ "sysroot.mount" ] else [ deviceUnit ]; 205 }; 206 }; 207 208 environment.etc = lib.mkIf cfg.enable { 209 "repart.d".source = definitionsDirectory; 210 }; 211 212 systemd = lib.mkIf cfg.enable { 213 additionalUpstreamSystemUnits = [ 214 "systemd-repart.service" 215 ]; 216 }; 217 }; 218 219 meta.maintainers = with lib.maintainers; [ nikstur ]; 220}