]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/gpt-auto-generator/gpt-auto-generator.c
service: add new RootImageOptions feature
[thirdparty/systemd.git] / src / gpt-auto-generator / gpt-auto-generator.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
1a14a53c 2
1a14a53c 3#include <stdlib.h>
cf0fbc49 4#include <unistd.h>
1a14a53c 5
85624f01 6#include "sd-device.h"
07630cea
LP
7#include "sd-id128.h"
8
b5efdb8a 9#include "alloc-util.h"
07630cea 10#include "blkid-util.h"
18c528e9 11#include "blockdev-util.h"
07630cea 12#include "btrfs-util.h"
133432cc 13#include "device-util.h"
a0956174 14#include "dirent-util.h"
72e18a98 15#include "dissect-image.h"
1fac34b9 16#include "dropin.h"
0bb2f0f1 17#include "efi-loader.h"
3ffd4af2 18#include "fd-util.h"
07630cea 19#include "fileio.h"
2bef2582 20#include "fs-util.h"
07630cea
LP
21#include "fstab-util.h"
22#include "generator.h"
23#include "gpt.h"
07630cea 24#include "mkdir.h"
049af8ad 25#include "mountpoint-util.h"
6bedfcbb 26#include "parse-util.h"
07630cea 27#include "path-util.h"
4e731273 28#include "proc-cmdline.h"
1a14a53c 29#include "special.h"
98bad05e 30#include "specifier.h"
8fcde012 31#include "stat-util.h"
07630cea 32#include "string-util.h"
85624f01 33#include "strv.h"
1a14a53c 34#include "unit-name.h"
07630cea 35#include "util.h"
9a5cb137 36#include "virt.h"
1a14a53c 37
ec6e9597 38static const char *arg_dest = NULL;
73b80ec2
LP
39static bool arg_enabled = true;
40static bool arg_root_enabled = true;
c94b2417 41static int arg_root_rw = -1;
1a14a53c 42
3d92aa45 43static int open_parent_block_device(dev_t devnum, int *ret_fd) {
9fe6f5cc
ZJS
44 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
45 const char *name, *devtype, *node;
46 sd_device *parent;
47 dev_t pn;
48 int fd, r;
49
3d92aa45 50 assert(ret_fd);
9fe6f5cc
ZJS
51
52 r = sd_device_new_from_devnum(&d, 'b', devnum);
53 if (r < 0)
54 return log_debug_errno(r, "Failed to open device: %m");
55
56 if (sd_device_get_devname(d, &name) < 0) {
57 r = sd_device_get_syspath(d, &name);
58 if (r < 0) {
3d92aa45
ZJS
59 log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m",
60 major(devnum), minor(devnum));
9fe6f5cc
ZJS
61 return 0;
62 }
63 }
64
65 r = sd_device_get_parent(d, &parent);
66 if (r < 0) {
67 log_device_debug_errno(d, r, "Not a partitioned device, ignoring: %m");
68 return 0;
69 }
70
71 /* Does it have a devtype? */
72 r = sd_device_get_devtype(parent, &devtype);
73 if (r < 0) {
74 log_device_debug_errno(parent, r, "Parent doesn't have a device type, ignoring: %m");
75 return 0;
76 }
77
78 /* Is this a disk or a partition? We only care for disks... */
79 if (!streq(devtype, "disk")) {
80 log_device_debug(parent, "Parent isn't a raw disk, ignoring.");
81 return 0;
82 }
83
84 /* Does it have a device node? */
85 r = sd_device_get_devname(parent, &node);
86 if (r < 0) {
87 log_device_debug_errno(parent, r, "Parent device does not have device node, ignoring: %m");
88 return 0;
89 }
90
91 log_device_debug(d, "Root device %s.", node);
92
93 r = sd_device_get_devnum(parent, &pn);
94 if (r < 0) {
95 log_device_debug_errno(parent, r, "Parent device is not a proper block device, ignoring: %m");
96 return 0;
97 }
98
99 fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
100 if (fd < 0)
101 return log_error_errno(errno, "Failed to open %s: %m", node);
102
3d92aa45 103 *ret_fd = fd;
9fe6f5cc
ZJS
104 return 1;
105}
106
01af8c01 107static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
a7e88558 108 _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
1af72119 109 _cleanup_fclose_ FILE *f = NULL;
1af72119
LP
110 int r;
111
112 assert(id);
113 assert(what);
1af72119 114
7410616c
LP
115 r = unit_name_from_path(what, ".device", &d);
116 if (r < 0)
117 return log_error_errno(r, "Failed to generate unit name: %m");
1af72119
LP
118
119 e = unit_name_escape(id);
120 if (!e)
121 return log_oom();
122
7410616c
LP
123 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
124 if (r < 0)
125 return log_error_errno(r, "Failed to generate unit name: %m");
1af72119 126
a7e88558
LP
127 r = generator_open_unit_file(arg_dest, NULL, n, &f);
128 if (r < 0)
129 return r;
98bad05e 130
a7e88558
LP
131 r = generator_write_cryptsetup_unit_section(f, NULL);
132 if (r < 0)
133 return r;
1af72119
LP
134
135 fprintf(f,
1af72119 136 "Before=umount.target cryptsetup.target\n"
a7e88558
LP
137 "Conflicts=umount.target\n"
138 "BindsTo=%s\n"
139 "After=%s\n",
140 d, d);
141
142 r = generator_write_cryptsetup_service_section(f, id, what, NULL, rw ? NULL : "read-only");
143 if (r < 0)
144 return r;
1af72119 145
dacd6cee
LP
146 r = fflush_and_check(f);
147 if (r < 0)
a7e88558 148 return log_error_errno(r, "Failed to write file %s: %m", n);
1af72119 149
9cdcf368
ZJS
150 r = generator_add_symlink(arg_dest, d, "wants", n);
151 if (r < 0)
152 return r;
1af72119 153
1fac34b9
ZJS
154 const char *dmname;
155 dmname = strjoina("dev-mapper-", e, ".device");
1af72119 156
1fac34b9 157 if (require) {
9cdcf368
ZJS
158 r = generator_add_symlink(arg_dest, "cryptsetup.target", "requires", n);
159 if (r < 0)
160 return r;
01af8c01 161
9cdcf368
ZJS
162 r = generator_add_symlink(arg_dest, dmname, "requires", n);
163 if (r < 0)
164 return r;
01af8c01 165 }
1af72119 166
1fac34b9
ZJS
167 r = write_drop_in_format(arg_dest, dmname, 50, "job-timeout",
168 "# Automatically generated by systemd-gpt-auto-generator\n\n"
169 "[Unit]\n"
170 "JobTimeoutSec=0"); /* the binary handles timeouts anyway */
23bbb0de 171 if (r < 0)
1fac34b9 172 log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
1af72119 173
2aa2860b
ZJS
174 if (device) {
175 char *ret;
176
b910cc72 177 ret = path_join("/dev/mapper", id);
2aa2860b
ZJS
178 if (!ret)
179 return log_oom();
1af72119 180
01af8c01 181 *device = ret;
2aa2860b
ZJS
182 }
183
1af72119
LP
184 return 0;
185}
186
73b80ec2
LP
187static int add_mount(
188 const char *id,
189 const char *what,
190 const char *where,
191 const char *fstype,
cca1dfdd 192 bool rw,
59512f21 193 const char *options,
73b80ec2
LP
194 const char *description,
195 const char *post) {
196
9cdcf368 197 _cleanup_free_ char *unit = NULL, *crypto_what = NULL, *p = NULL;
1a14a53c 198 _cleanup_fclose_ FILE *f = NULL;
e48fdd84 199 int r;
1a14a53c 200
98bad05e
LP
201 /* Note that we don't apply specifier escaping on the input strings here, since we know they are not configured
202 * externally, but all originate from our own sources here, and hence we know they contain no % characters that
203 * could potentially be understood as specifiers. */
204
1af72119
LP
205 assert(id);
206 assert(what);
207 assert(where);
1af72119
LP
208 assert(description);
209
074cdb95 210 log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
1a14a53c 211
73b80ec2 212 if (streq_ptr(fstype, "crypto_LUKS")) {
01af8c01 213 r = add_cryptsetup(id, what, rw, true, &crypto_what);
1af72119
LP
214 if (r < 0)
215 return r;
216
217 what = crypto_what;
218 fstype = NULL;
219 }
220
7410616c
LP
221 r = unit_name_from_path(where, ".mount", &unit);
222 if (r < 0)
223 return log_error_errno(r, "Failed to generate unit name: %m");
1a14a53c 224
657ee2d8 225 p = path_join(empty_to_root(arg_dest), unit);
e48fdd84
LP
226 if (!p)
227 return log_oom();
228
229 f = fopen(p, "wxe");
4a62c710
MS
230 if (!f)
231 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
1a14a53c
LP
232
233 fprintf(f,
234 "# Automatically generated by systemd-gpt-auto-generator\n\n"
235 "[Unit]\n"
c3834f9b
LP
236 "Description=%s\n"
237 "Documentation=man:systemd-gpt-auto-generator(8)\n",
e48fdd84
LP
238 description);
239
73b80ec2
LP
240 if (post)
241 fprintf(f, "Before=%s\n", post);
242
e48fdd84
LP
243 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
244 if (r < 0)
245 return r;
246
a7e88558
LP
247 r = generator_write_blockdev_dependency(f, what);
248 if (r < 0)
249 return r;
250
e48fdd84
LP
251 fprintf(f,
252 "\n"
1a14a53c
LP
253 "[Mount]\n"
254 "What=%s\n"
1af72119
LP
255 "Where=%s\n",
256 what, where);
257
73b80ec2
LP
258 if (fstype)
259 fprintf(f, "Type=%s\n", fstype);
260
59512f21
KS
261 if (options)
262 fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro");
263 else
264 fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
1a14a53c 265
dacd6cee
LP
266 r = fflush_and_check(f);
267 if (r < 0)
268 return log_error_errno(r, "Failed to write unit file %s: %m", p);
1a14a53c 269
9cdcf368
ZJS
270 if (post)
271 return generator_add_symlink(arg_dest, post, "requires", unit);
1a14a53c
LP
272 return 0;
273}
274
e137880b 275static int path_is_busy(const char *where) {
59512f21
KS
276 int r;
277
278 /* already a mountpoint; generators run during reload */
e1873695 279 r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW);
59512f21
KS
280 if (r > 0)
281 return false;
282
283 /* the directory might not exist on a stateless system */
284 if (r == -ENOENT)
285 return false;
286
287 if (r < 0)
e137880b 288 return log_warning_errno(r, "Cannot check if \"%s\" is a mount point: %m", where);
59512f21
KS
289
290 /* not a mountpoint but it contains files */
e137880b
ZJS
291 r = dir_is_empty(where);
292 if (r < 0)
293 return log_warning_errno(r, "Cannot check if \"%s\" is empty: %m", where);
294 if (r > 0)
295 return false;
59512f21 296
e137880b
ZJS
297 log_debug("\"%s\" already populated, ignoring.", where);
298 return true;
59512f21
KS
299}
300
72e18a98
LP
301static int add_partition_mount(
302 DissectedPartition *p,
61331eab 303 const char *id,
61331eab 304 const char *where,
72e18a98 305 const char *description) {
61331eab 306
e137880b 307 int r;
72e18a98 308 assert(p);
61331eab 309
e137880b
ZJS
310 r = path_is_busy(where);
311 if (r != 0)
312 return r < 0 ? r : 0;
61331eab 313
61331eab
LP
314 return add_mount(
315 id,
72e18a98 316 p->node,
61331eab 317 where,
72e18a98
LP
318 p->fstype,
319 p->rw,
59512f21 320 NULL,
61331eab 321 description,
72e18a98 322 SPECIAL_LOCAL_FS_TARGET);
61331eab
LP
323}
324
59512f21 325static int add_swap(const char *path) {
9cdcf368 326 _cleanup_free_ char *name = NULL, *unit = NULL;
59512f21
KS
327 _cleanup_fclose_ FILE *f = NULL;
328 int r;
329
330 assert(path);
331
fc5bc384
FB
332 /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */
333 r = fstab_has_fstype("swap");
334 if (r < 0)
335 return log_error_errno(r, "Failed to parse fstab: %m");
1a680ae3 336 if (r > 0) {
fc5bc384
FB
337 log_debug("swap specified in fstab, ignoring.");
338 return 0;
339 }
340
59512f21
KS
341 log_debug("Adding swap: %s", path);
342
343 r = unit_name_from_path(path, ".swap", &name);
344 if (r < 0)
345 return log_error_errno(r, "Failed to generate unit name: %m");
346
657ee2d8 347 unit = path_join(empty_to_root(arg_dest), name);
59512f21
KS
348 if (!unit)
349 return log_oom();
350
351 f = fopen(unit, "wxe");
352 if (!f)
353 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
354
355 fprintf(f,
356 "# Automatically generated by systemd-gpt-auto-generator\n\n"
357 "[Unit]\n"
358 "Description=Swap Partition\n"
a7e88558
LP
359 "Documentation=man:systemd-gpt-auto-generator(8)\n");
360
361 r = generator_write_blockdev_dependency(f, path);
362 if (r < 0)
363 return r;
364
365 fprintf(f,
366 "\n"
59512f21
KS
367 "[Swap]\n"
368 "What=%s\n",
369 path);
370
dacd6cee
LP
371 r = fflush_and_check(f);
372 if (r < 0)
373 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
59512f21 374
9cdcf368 375 return generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, "wants", name);
59512f21
KS
376}
377
7a1494aa
TG
378static int add_automount(
379 const char *id,
380 const char *what,
381 const char *where,
382 const char *fstype,
383 bool rw,
384 const char *options,
385 const char *description,
386 usec_t timeout) {
387
9cdcf368 388 _cleanup_free_ char *unit = NULL;
7a1494aa 389 _cleanup_fclose_ FILE *f = NULL;
2aa2860b 390 const char *opt = "noauto", *p;
7a1494aa
TG
391 int r;
392
393 assert(id);
394 assert(where);
395 assert(description);
396
397 if (options)
2aa2860b 398 opt = strjoina(options, ",", opt);
7a1494aa
TG
399
400 r = add_mount(id,
401 what,
402 where,
403 fstype,
404 rw,
405 opt,
406 description,
407 NULL);
408 if (r < 0)
409 return r;
410
411 r = unit_name_from_path(where, ".automount", &unit);
412 if (r < 0)
413 return log_error_errno(r, "Failed to generate unit name: %m");
414
270384b2 415 p = prefix_roota(arg_dest, unit);
7a1494aa
TG
416 f = fopen(p, "wxe");
417 if (!f)
418 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
419
420 fprintf(f,
421 "# Automatically generated by systemd-gpt-auto-generator\n\n"
422 "[Unit]\n"
423 "Description=%s\n"
424 "Documentation=man:systemd-gpt-auto-generator(8)\n"
425 "[Automount]\n"
426 "Where=%s\n"
70887c5f 427 "TimeoutIdleSec="USEC_FMT"\n",
7a1494aa
TG
428 description,
429 where,
70887c5f 430 timeout / USEC_PER_SEC);
7a1494aa
TG
431
432 r = fflush_and_check(f);
433 if (r < 0)
434 return log_error_errno(r, "Failed to write unit file %s: %m", p);
435
9cdcf368 436 return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
7a1494aa
TG
437}
438
4f084066
LP
439static const char *esp_or_xbootldr_options(const DissectedPartition *p) {
440 assert(p);
441
442 /* if we probed vfat or have no idea about the file system then assume these file systems are vfat
443 * and thus understand "umask=0077". If we detected something else then don't specify any options and
444 * use kernel defaults. */
445
446 if (!p->fstype || streq(p->fstype, "vfat"))
447 return "umask=0077";
448
449 return NULL;
450}
451
9f1cb0c1
LP
452static int add_xbootldr(DissectedPartition *p) {
453 int r;
454
455 assert(p);
456
457 if (in_initrd()) {
458 log_debug("In initrd, ignoring the XBOOTLDR partition.");
459 return 0;
460 }
461
462 r = fstab_is_mount_point("/boot");
463 if (r < 0)
464 return log_error_errno(r, "Failed to parse fstab: %m");
465 if (r > 0) {
466 log_debug("/boot specified in fstab, ignoring XBOOTLDR partition.");
467 return 0;
468 }
469
470 r = path_is_busy("/boot");
471 if (r < 0)
472 return r;
473 if (r > 0)
474 return 0;
475
476 return add_automount("boot",
477 p->node,
478 "/boot",
479 p->fstype,
480 true,
4f084066 481 esp_or_xbootldr_options(p),
9f1cb0c1
LP
482 "Boot Loader Partition",
483 120 * USEC_PER_SEC);
484}
485
486#if ENABLE_EFI
487static int add_esp(DissectedPartition *p, bool has_xbootldr) {
488 const char *esp_path = NULL, *id = NULL;
59512f21
KS
489 int r;
490
72e18a98 491 assert(p);
59512f21 492
59512f21 493 if (in_initrd()) {
b52a109a 494 log_debug("In initrd, ignoring the ESP.");
59512f21
KS
495 return 0;
496 }
497
9f1cb0c1
LP
498 /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice, but
499 * only if there's no explicit XBOOTLDR partition around. */
500 if (access("/efi", F_OK) < 0) {
501 if (errno != ENOENT)
502 return log_error_errno(errno, "Failed to determine whether /efi exists: %m");
503
504 /* Use /boot as fallback, but only if there's no XBOOTLDR partition */
505 if (!has_xbootldr) {
506 esp_path = "/boot";
507 id = "boot";
508 }
509 }
510 if (!esp_path)
511 esp_path = "/efi";
512 if (!id)
513 id = "efi";
59512f21 514
0b6b6787 515 /* We create an .automount which is not overridden by the .mount from the fstab generator. */
9f1cb0c1 516 r = fstab_is_mount_point(esp_path);
b9088048
FB
517 if (r < 0)
518 return log_error_errno(r, "Failed to parse fstab: %m");
39b6a511 519 if (r > 0) {
9f1cb0c1 520 log_debug("%s specified in fstab, ignoring.", esp_path);
59512f21
KS
521 return 0;
522 }
523
9f1cb0c1
LP
524 r = path_is_busy(esp_path);
525 if (r < 0)
526 return r;
527 if (r > 0)
528 return 0;
59512f21 529
7ba25ab5 530 if (is_efi_boot()) {
72e18a98 531 sd_id128_t loader_uuid;
59512f21 532
7ba25ab5 533 /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
59512f21 534
7ba25ab5
LP
535 r = efi_loader_get_device_part_uuid(&loader_uuid);
536 if (r == -ENOENT) {
537 log_debug("EFI loader partition unknown.");
538 return 0;
539 }
e28973ee
ZJS
540 if (r < 0)
541 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
7ba25ab5 542
72e18a98 543 if (!sd_id128_equal(p->uuid, loader_uuid)) {
9f1cb0c1 544 log_debug("Partition for %s does not appear to be the partition we are booted from.", p->node);
7ba25ab5
LP
545 return 0;
546 }
547 } else
548 log_debug("Not an EFI boot, skipping ESP check.");
549
9f1cb0c1 550 return add_automount(id,
72e18a98 551 p->node,
9f1cb0c1 552 esp_path,
72e18a98
LP
553 p->fstype,
554 true,
4f084066 555 esp_or_xbootldr_options(p),
72e18a98
LP
556 "EFI System Partition Automount",
557 120 * USEC_PER_SEC);
7a1494aa 558}
59512f21 559#else
9f1cb0c1 560static int add_esp(DissectedPartition *p, bool has_xbootldr) {
59512f21 561 return 0;
59512f21 562}
7a1494aa 563#endif
59512f21 564
fd89051e
LP
565static int add_root_rw(DissectedPartition *p) {
566 const char *path;
567 int r;
568
569 assert(p);
570
571 if (in_initrd()) {
572 log_debug("In initrd, not generating drop-in for systemd-remount-fs.service.");
573 return 0;
574 }
575
576 if (arg_root_rw >= 0) {
577 log_debug("Parameter ro/rw specified on kernel command line, not generating drop-in for systemd-remount-fs.service.");
578 return 0;
579 }
580
581 if (!p->rw) {
582 log_debug("Root partition marked read-only in GPT partition table, not generating drop-in for systemd-remount-fs.service.");
583 return 0;
584 }
585
9b69569d
ZJS
586 (void) generator_enable_remount_fs_service(arg_dest);
587
fd89051e 588 path = strjoina(arg_dest, "/systemd-remount-fs.service.d/50-remount-rw.conf");
fd89051e
LP
589
590 r = write_string_file(path,
591 "# Automatically generated by systemd-gpt-generator\n\n"
fd89051e
LP
592 "[Service]\n"
593 "Environment=SYSTEMD_REMOUNT_ROOT_RW=1\n",
e82e549f 594 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_NOFOLLOW|WRITE_STRING_FILE_MKDIR_0755);
fd89051e
LP
595 if (r < 0)
596 return log_error_errno(r, "Failed to write drop-in file %s: %m", path);
597
598 return 0;
599}
600
9fe6f5cc
ZJS
601#if ENABLE_EFI
602static int add_root_cryptsetup(void) {
1a14a53c 603
9fe6f5cc
ZJS
604 /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
605 * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
1a14a53c 606
9fe6f5cc
ZJS
607 return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
608}
609#endif
d2a62382 610
9fe6f5cc
ZJS
611static int add_root_mount(void) {
612#if ENABLE_EFI
613 int r;
1a14a53c 614
9fe6f5cc
ZJS
615 if (!is_efi_boot()) {
616 log_debug("Not a EFI boot, not creating root mount.");
8090b41e 617 return 0;
fa041593 618 }
61331eab 619
9fe6f5cc
ZJS
620 r = efi_loader_get_device_part_uuid(NULL);
621 if (r == -ENOENT) {
b50a3a15
ZJS
622 log_notice("EFI loader partition unknown, exiting.\n"
623 "(The boot loader did not set EFI variable LoaderDevicePartUUID.)");
8090b41e 624 return 0;
9fe6f5cc
ZJS
625 } else if (r < 0)
626 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
61331eab 627
9fe6f5cc
ZJS
628 /* OK, we have an ESP partition, this is fantastic, so let's
629 * wait for a root device to show up. A udev rule will create
630 * the link for us under the right name. */
61331eab 631
9fe6f5cc
ZJS
632 if (in_initrd()) {
633 r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
634 if (r < 0)
635 return 0;
61331eab 636
9fe6f5cc
ZJS
637 r = add_root_cryptsetup();
638 if (r < 0)
639 return r;
61331eab
LP
640 }
641
9fe6f5cc
ZJS
642 /* Note that we do not need to enable systemd-remount-fs.service here. If
643 * /etc/fstab exists, systemd-fstab-generator will pull it in for us. */
61331eab 644
9fe6f5cc
ZJS
645 return add_mount(
646 "root",
647 "/dev/gpt-auto-root",
648 in_initrd() ? "/sysroot" : "/",
649 NULL,
650 arg_root_rw > 0,
651 NULL,
652 "Root Partition",
653 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
654#else
655 return 0;
656#endif
72e18a98 657}
cb971249 658
72e18a98 659static int enumerate_partitions(dev_t devnum) {
72e18a98
LP
660 _cleanup_close_ int fd = -1;
661 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
662 int r, k;
61331eab 663
3d92aa45 664 r = open_parent_block_device(devnum, &fd);
72e18a98
LP
665 if (r <= 0)
666 return r;
61331eab 667
18d73705 668 r = dissect_image(fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
72e18a98
LP
669 if (r == -ENOPKG) {
670 log_debug_errno(r, "No suitable partition table found, ignoring.");
671 return 0;
61331eab 672 }
23bbb0de 673 if (r < 0)
72e18a98 674 return log_error_errno(r, "Failed to dissect: %m");
0238d4c6 675
72e18a98
LP
676 if (m->partitions[PARTITION_SWAP].found) {
677 k = add_swap(m->partitions[PARTITION_SWAP].node);
678 if (k < 0)
679 r = k;
1a14a53c
LP
680 }
681
9f1cb0c1
LP
682 if (m->partitions[PARTITION_XBOOTLDR].found) {
683 k = add_xbootldr(m->partitions + PARTITION_XBOOTLDR);
684 if (k < 0)
685 r = k;
686 }
687
72e18a98 688 if (m->partitions[PARTITION_ESP].found) {
9f1cb0c1 689 k = add_esp(m->partitions + PARTITION_ESP, m->partitions[PARTITION_XBOOTLDR].found);
59512f21
KS
690 if (k < 0)
691 r = k;
692 }
693
72e18a98
LP
694 if (m->partitions[PARTITION_HOME].found) {
695 k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition");
73b80ec2
LP
696 if (k < 0)
697 r = k;
698 }
e48fdd84 699
72e18a98
LP
700 if (m->partitions[PARTITION_SRV].found) {
701 k = add_partition_mount(m->partitions + PARTITION_SRV, "srv", "/srv", "Server Data Partition");
73b80ec2
LP
702 if (k < 0)
703 r = k;
704 }
1a14a53c 705
d4dffb85
LP
706 if (m->partitions[PARTITION_VAR].found) {
707 k = add_partition_mount(m->partitions + PARTITION_VAR, "var", "/var", "Variable Data Partition");
708 if (k < 0)
709 r = k;
710 }
711
712 if (m->partitions[PARTITION_TMP].found) {
713 k = add_partition_mount(m->partitions + PARTITION_TMP, "var-tmp", "/var/tmp", "Temporary Data Partition");
714 if (k < 0)
715 r = k;
716 }
717
fd89051e
LP
718 if (m->partitions[PARTITION_ROOT].found) {
719 k = add_root_rw(m->partitions + PARTITION_ROOT);
720 if (k < 0)
721 r = k;
722 }
723
1a14a53c
LP
724 return r;
725}
726
9fe6f5cc
ZJS
727static int add_mounts(void) {
728 dev_t devno;
729 int r;
730
731 r = get_block_device_harder("/", &devno);
732 if (r < 0)
733 return log_error_errno(r, "Failed to determine block device of root file system: %m");
734 if (r == 0) {
735 r = get_block_device_harder("/usr", &devno);
736 if (r < 0)
737 return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
738 if (r == 0) {
739 _cleanup_free_ char *p = NULL;
740 mode_t m;
741
742 /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
743 * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
744 * here. */
745 r = readlink_malloc("/run/systemd/volatile-root", &p);
746 if (r == -ENOENT) {
747 log_debug("Neither root nor /usr file system are on a (single) block device.");
748 return 0;
749 }
750 if (r < 0)
751 return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
752
753 r = device_path_parse_major_minor(p, &m, &devno);
754 if (r < 0)
755 return log_error_errno(r, "Failed to parse major/minor device node: %m");
756 if (!S_ISBLK(m))
757 return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
758 }
759 }
760
761 return enumerate_partitions(devno);
762}
763
96287a49 764static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
73b80ec2 765 int r;
1a14a53c 766
73b80ec2 767 assert(key);
1a14a53c 768
8a9c44ed
LP
769 if (proc_cmdline_key_streq(key, "systemd.gpt_auto") ||
770 proc_cmdline_key_streq(key, "rd.systemd.gpt_auto")) {
1a14a53c 771
1d84ad94 772 r = value ? parse_boolean(value) : 1;
73b80ec2 773 if (r < 0)
0a1b9449 774 log_warning_errno(r, "Failed to parse gpt-auto switch \"%s\", ignoring: %m", value);
8086ffac
ZJS
775 else
776 arg_enabled = r;
1a14a53c 777
8a9c44ed 778 } else if (proc_cmdline_key_streq(key, "root")) {
1d84ad94
LP
779
780 if (proc_cmdline_value_missing(key, value))
781 return 0;
73b80ec2
LP
782
783 /* Disable root disk logic if there's a root= value
784 * specified (unless it happens to be "gpt-auto") */
785
074cdb95
ZJS
786 if (!streq(value, "gpt-auto")) {
787 arg_root_enabled = false;
788 log_debug("Disabling root partition auto-detection, root= is defined.");
789 }
73b80ec2 790
8a9c44ed 791 } else if (proc_cmdline_key_streq(key, "roothash")) {
2f3dfc6f
LP
792
793 if (proc_cmdline_value_missing(key, value))
794 return 0;
795
796 /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
797
798 arg_root_enabled = false;
799
8a9c44ed 800 } else if (proc_cmdline_key_streq(key, "rw") && !value)
73b80ec2 801 arg_root_rw = true;
8a9c44ed 802 else if (proc_cmdline_key_streq(key, "ro") && !value)
73b80ec2 803 arg_root_rw = false;
73b80ec2
LP
804
805 return 0;
806}
807
ec6e9597 808static int run(const char *dest, const char *dest_early, const char *dest_late) {
8f50e86a 809 int r, k;
73b80ec2 810
ec6e9597 811 assert_se(arg_dest = dest_late);
73b80ec2 812
75f86906 813 if (detect_container() > 0) {
73b80ec2 814 log_debug("In a container, exiting.");
ec6e9597 815 return 0;
1a14a53c 816 }
3db604b9 817
1d84ad94 818 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
b5884878 819 if (r < 0)
da927ba9 820 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
1a14a53c 821
73b80ec2
LP
822 if (!arg_enabled) {
823 log_debug("Disabled, exiting.");
ec6e9597 824 return 0;
73b80ec2
LP
825 }
826
827 if (arg_root_enabled)
828 r = add_root_mount();
829
830 if (!in_initrd()) {
73b80ec2 831 k = add_mounts();
ec6e9597 832 if (r >= 0)
73b80ec2
LP
833 r = k;
834 }
835
ec6e9597 836 return r;
1a14a53c 837}
ec6e9597
ZJS
838
839DEFINE_MAIN_GENERATOR_FUNCTION(run);