1{ config, lib, pkgs, utils, ... }:
2
3let
4 cfg = config.systemd.repart;
5 initrdCfg = config.boot.initrd.systemd.repart;
6
7 format = pkgs.formats.ini { };
8
9 definitionsDirectory = utils.systemdUtils.lib.definitions
10 "repart.d"
11 format
12 (lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions);
13in
14{
15 options = {
16 boot.initrd.systemd.repart = {
17 enable = lib.mkEnableOption (lib.mdDoc "systemd-repart") // {
18 description = lib.mdDoc ''
19 Grow and add partitions to a partition table at boot time in the initrd.
20 systemd-repart only works with GPT partition tables.
21
22 To run systemd-repart after the initrd, see
23 `options.systemd.repart.enable`.
24 '';
25 };
26
27 device = lib.mkOption {
28 type = with lib.types; nullOr str;
29 description = lib.mdDoc ''
30 The device to operate on.
31
32 If `device == null`, systemd-repart will operate on the device
33 backing the root partition. So in order to dynamically *create* the
34 root partition in the initrd you need to set a device.
35 '';
36 default = null;
37 example = "/dev/vda";
38 };
39 };
40
41 systemd.repart = {
42 enable = lib.mkEnableOption (lib.mdDoc "systemd-repart") // {
43 description = lib.mdDoc ''
44 Grow and add partitions to a partition table.
45 systemd-repart only works with GPT partition tables.
46
47 To run systemd-repart while in the initrd, see
48 `options.boot.initrd.systemd.repart.enable`.
49 '';
50 };
51
52 partitions = lib.mkOption {
53 type = with lib.types; attrsOf (attrsOf (oneOf [ str int bool ]));
54 default = { };
55 example = {
56 "10-root" = {
57 Type = "root";
58 };
59 "20-home" = {
60 Type = "home";
61 SizeMinBytes = "512M";
62 SizeMaxBytes = "2G";
63 };
64 };
65 description = lib.mdDoc ''
66 Specify partitions as a set of the names of the definition files as the
67 key and the partition configuration as its value. The partition
68 configuration can use all upstream options. See <link
69 xlink:href="https://www.freedesktop.org/software/systemd/man/repart.d.html"/>
70 for all available options.
71 '';
72 };
73 };
74 };
75
76 config = lib.mkIf (cfg.enable || initrdCfg.enable) {
77 assertions = [
78 {
79 assertion = initrdCfg.enable -> config.boot.initrd.systemd.enable;
80 message = ''
81 'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled.
82 '';
83 }
84 ];
85
86 boot.initrd.systemd = lib.mkIf initrdCfg.enable {
87 additionalUpstreamUnits = [
88 "systemd-repart.service"
89 ];
90
91 storePaths = [
92 "${config.boot.initrd.systemd.package}/bin/systemd-repart"
93 ];
94
95 contents."/etc/repart.d".source = definitionsDirectory;
96
97 # Override defaults in upstream unit.
98 services.systemd-repart =
99 let
100 deviceUnit = "${utils.escapeSystemdPath initrdCfg.device}.device";
101 in
102 {
103 # systemd-repart tries to create directories in /var/tmp by default to
104 # store large temporary files that benefit from persistence on disk. In
105 # the initrd, however, /var/tmp does not provide more persistence than
106 # /tmp, so we re-use it here.
107 environment."TMPDIR" = "/tmp";
108 serviceConfig = {
109 ExecStart = [
110 " " # required to unset the previous value.
111 # When running in the initrd, systemd-repart by default searches
112 # for definition files in /sysroot or /sysusr. We tell it to look
113 # in the initrd itself.
114 ''${config.boot.initrd.systemd.package}/bin/systemd-repart \
115 --definitions=/etc/repart.d \
116 --dry-run=no ${lib.optionalString (initrdCfg.device != null) initrdCfg.device}
117 ''
118 ];
119 };
120 # systemd-repart needs to run after /sysroot (or /sysuser, but we
121 # don't have it) has been mounted because otherwise it cannot
122 # determine the device (i.e disk) to operate on. If you want to run
123 # systemd-repart without /sysroot (i.e. to create the root
124 # partition), you have to explicitly tell it which device to operate
125 # on. The service then needs to be ordered to run after this device
126 # is available.
127 requires = lib.mkIf (initrdCfg.device != null) [ deviceUnit ];
128 after =
129 if initrdCfg.device == null then
130 [ "sysroot.mount" ]
131 else
132 [ deviceUnit ];
133 };
134 };
135
136 environment.etc = lib.mkIf cfg.enable {
137 "repart.d".source = definitionsDirectory;
138 };
139
140 systemd = lib.mkIf cfg.enable {
141 additionalUpstreamSystemUnits = [
142 "systemd-repart.service"
143 ];
144 };
145 };
146
147 meta.maintainers = with lib.maintainers; [ nikstur ];
148}