]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/gpt-auto-generator/gpt-auto-generator.c
fstab-util: introduce fstab_has_fstype() helper
[thirdparty/systemd.git] / src / gpt-auto-generator / gpt-auto-generator.c
CommitLineData
1a14a53c
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2013 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
6b5cf3ea 20#include <blkid.h>
1a14a53c 21#include <stdlib.h>
1a14a53c 22#include <sys/statfs.h>
cf0fbc49 23#include <unistd.h>
1a14a53c 24
b4bbcaa9 25#include "libudev.h"
07630cea
LP
26#include "sd-id128.h"
27
b5efdb8a 28#include "alloc-util.h"
07630cea
LP
29#include "blkid-util.h"
30#include "btrfs-util.h"
a0956174 31#include "dirent-util.h"
72e18a98 32#include "dissect-image.h"
07630cea 33#include "efivars.h"
3ffd4af2 34#include "fd-util.h"
07630cea
LP
35#include "fileio.h"
36#include "fstab-util.h"
37#include "generator.h"
38#include "gpt.h"
1a14a53c 39#include "missing.h"
07630cea 40#include "mkdir.h"
4349cd7c 41#include "mount-util.h"
6bedfcbb 42#include "parse-util.h"
07630cea 43#include "path-util.h"
4e731273 44#include "proc-cmdline.h"
1a14a53c 45#include "special.h"
8fcde012 46#include "stat-util.h"
07630cea
LP
47#include "string-util.h"
48#include "udev-util.h"
1a14a53c 49#include "unit-name.h"
07630cea 50#include "util.h"
9a5cb137 51#include "virt.h"
1a14a53c 52
1a14a53c 53static const char *arg_dest = "/tmp";
73b80ec2
LP
54static bool arg_enabled = true;
55static bool arg_root_enabled = true;
56static bool arg_root_rw = false;
1a14a53c 57
01af8c01 58static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
1af72119
LP
59 _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
60 _cleanup_fclose_ FILE *f = NULL;
61 char *from, *ret;
62 int r;
63
64 assert(id);
65 assert(what);
1af72119 66
7410616c
LP
67 r = unit_name_from_path(what, ".device", &d);
68 if (r < 0)
69 return log_error_errno(r, "Failed to generate unit name: %m");
1af72119
LP
70
71 e = unit_name_escape(id);
72 if (!e)
73 return log_oom();
74
7410616c
LP
75 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
76 if (r < 0)
77 return log_error_errno(r, "Failed to generate unit name: %m");
1af72119 78
605405c6 79 p = strjoin(arg_dest, "/", n);
821b2e79 80 if (!p)
1af72119
LP
81 return log_oom();
82
83 f = fopen(p, "wxe");
4a62c710
MS
84 if (!f)
85 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
1af72119
LP
86
87 fprintf(f,
88 "# Automatically generated by systemd-gpt-auto-generator\n\n"
89 "[Unit]\n"
90 "Description=Cryptography Setup for %%I\n"
c3834f9b 91 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
1af72119
LP
92 "DefaultDependencies=no\n"
93 "Conflicts=umount.target\n"
94 "BindsTo=dev-mapper-%%i.device %s\n"
95 "Before=umount.target cryptsetup.target\n"
96 "After=%s\n"
97 "IgnoreOnIsolate=true\n"
1af72119
LP
98 "[Service]\n"
99 "Type=oneshot\n"
100 "RemainAfterExit=yes\n"
101 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
cca1dfdd 102 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
1af72119
LP
103 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
104 d, d,
cca1dfdd 105 id, what, rw ? "" : "read-only",
1af72119
LP
106 id);
107
dacd6cee
LP
108 r = fflush_and_check(f);
109 if (r < 0)
110 return log_error_errno(r, "Failed to write file %s: %m", p);
1af72119 111
63c372cb 112 from = strjoina("../", n);
1af72119 113
605405c6 114 to = strjoin(arg_dest, "/", d, ".wants/", n);
1af72119
LP
115 if (!to)
116 return log_oom();
117
118 mkdir_parents_label(to, 0755);
4a62c710
MS
119 if (symlink(from, to) < 0)
120 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
1af72119 121
01af8c01
LP
122 if (require) {
123 free(to);
1af72119 124
01af8c01
LP
125 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n);
126 if (!to)
127 return log_oom();
1af72119 128
01af8c01
LP
129 mkdir_parents_label(to, 0755);
130 if (symlink(from, to) < 0)
131 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
1af72119 132
01af8c01
LP
133 free(to);
134 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n);
135 if (!to)
136 return log_oom();
137
138 mkdir_parents_label(to, 0755);
139 if (symlink(from, to) < 0)
140 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
141 }
1af72119
LP
142
143 free(p);
605405c6 144 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf");
1af72119
LP
145 if (!p)
146 return log_oom();
147
148 mkdir_parents_label(p, 0755);
149 r = write_string_file(p,
150 "# Automatically generated by systemd-gpt-auto-generator\n\n"
151 "[Unit]\n"
4c1fc3e4
DM
152 "JobTimeoutSec=0\n",
153 WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */
23bbb0de
MS
154 if (r < 0)
155 return log_error_errno(r, "Failed to write device drop-in: %m");
1af72119
LP
156
157 ret = strappend("/dev/mapper/", id);
158 if (!ret)
159 return log_oom();
160
01af8c01
LP
161 if (device)
162 *device = ret;
1af72119
LP
163 return 0;
164}
165
73b80ec2
LP
166static int add_mount(
167 const char *id,
168 const char *what,
169 const char *where,
170 const char *fstype,
cca1dfdd 171 bool rw,
59512f21 172 const char *options,
73b80ec2
LP
173 const char *description,
174 const char *post) {
175
1af72119 176 _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
1a14a53c 177 _cleanup_fclose_ FILE *f = NULL;
e48fdd84 178 int r;
1a14a53c 179
1af72119
LP
180 assert(id);
181 assert(what);
182 assert(where);
1af72119
LP
183 assert(description);
184
73b80ec2 185 log_debug("Adding %s: %s %s", where, what, strna(fstype));
1a14a53c 186
73b80ec2 187 if (streq_ptr(fstype, "crypto_LUKS")) {
1af72119 188
01af8c01 189 r = add_cryptsetup(id, what, rw, true, &crypto_what);
1af72119
LP
190 if (r < 0)
191 return r;
192
193 what = crypto_what;
194 fstype = NULL;
195 }
196
7410616c
LP
197 r = unit_name_from_path(where, ".mount", &unit);
198 if (r < 0)
199 return log_error_errno(r, "Failed to generate unit name: %m");
1a14a53c 200
605405c6 201 p = strjoin(arg_dest, "/", unit);
e48fdd84
LP
202 if (!p)
203 return log_oom();
204
205 f = fopen(p, "wxe");
4a62c710
MS
206 if (!f)
207 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
1a14a53c
LP
208
209 fprintf(f,
210 "# Automatically generated by systemd-gpt-auto-generator\n\n"
211 "[Unit]\n"
c3834f9b
LP
212 "Description=%s\n"
213 "Documentation=man:systemd-gpt-auto-generator(8)\n",
e48fdd84
LP
214 description);
215
73b80ec2
LP
216 if (post)
217 fprintf(f, "Before=%s\n", post);
218
e48fdd84
LP
219 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
220 if (r < 0)
221 return r;
222
223 fprintf(f,
224 "\n"
1a14a53c
LP
225 "[Mount]\n"
226 "What=%s\n"
1af72119
LP
227 "Where=%s\n",
228 what, where);
229
73b80ec2
LP
230 if (fstype)
231 fprintf(f, "Type=%s\n", fstype);
232
59512f21
KS
233 if (options)
234 fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro");
235 else
236 fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
1a14a53c 237
dacd6cee
LP
238 r = fflush_and_check(f);
239 if (r < 0)
240 return log_error_errno(r, "Failed to write unit file %s: %m", p);
1a14a53c 241
73b80ec2 242 if (post) {
605405c6 243 lnk = strjoin(arg_dest, "/", post, ".requires/", unit);
73b80ec2
LP
244 if (!lnk)
245 return log_oom();
1a14a53c 246
73b80ec2 247 mkdir_parents_label(lnk, 0755);
4a62c710
MS
248 if (symlink(p, lnk) < 0)
249 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
1a14a53c
LP
250 }
251
252 return 0;
253}
254
59512f21
KS
255static bool path_is_busy(const char *where) {
256 int r;
257
258 /* already a mountpoint; generators run during reload */
e1873695 259 r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW);
59512f21
KS
260 if (r > 0)
261 return false;
262
263 /* the directory might not exist on a stateless system */
264 if (r == -ENOENT)
265 return false;
266
267 if (r < 0)
268 return true;
269
270 /* not a mountpoint but it contains files */
271 if (dir_is_empty(where) <= 0)
272 return true;
273
274 return false;
275}
276
72e18a98
LP
277static int add_partition_mount(
278 DissectedPartition *p,
61331eab 279 const char *id,
61331eab 280 const char *where,
72e18a98 281 const char *description) {
61331eab 282
72e18a98 283 assert(p);
61331eab 284
59512f21 285 if (path_is_busy(where)) {
61331eab
LP
286 log_debug("%s already populated, ignoring.", where);
287 return 0;
288 }
289
61331eab
LP
290 return add_mount(
291 id,
72e18a98 292 p->node,
61331eab 293 where,
72e18a98
LP
294 p->fstype,
295 p->rw,
59512f21 296 NULL,
61331eab 297 description,
72e18a98 298 SPECIAL_LOCAL_FS_TARGET);
61331eab
LP
299}
300
59512f21
KS
301static int add_swap(const char *path) {
302 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
303 _cleanup_fclose_ FILE *f = NULL;
304 int r;
305
306 assert(path);
307
308 log_debug("Adding swap: %s", path);
309
310 r = unit_name_from_path(path, ".swap", &name);
311 if (r < 0)
312 return log_error_errno(r, "Failed to generate unit name: %m");
313
605405c6 314 unit = strjoin(arg_dest, "/", name);
59512f21
KS
315 if (!unit)
316 return log_oom();
317
318 f = fopen(unit, "wxe");
319 if (!f)
320 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
321
322 fprintf(f,
323 "# Automatically generated by systemd-gpt-auto-generator\n\n"
324 "[Unit]\n"
325 "Description=Swap Partition\n"
326 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
327 "[Swap]\n"
328 "What=%s\n",
329 path);
330
dacd6cee
LP
331 r = fflush_and_check(f);
332 if (r < 0)
333 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
59512f21 334
605405c6 335 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name);
59512f21
KS
336 if (!lnk)
337 return log_oom();
338
339 mkdir_parents_label(lnk, 0755);
340 if (symlink(unit, lnk) < 0)
341 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
342
343 return 0;
344}
345
59512f21 346#ifdef ENABLE_EFI
7a1494aa
TG
347static int add_automount(
348 const char *id,
349 const char *what,
350 const char *where,
351 const char *fstype,
352 bool rw,
353 const char *options,
354 const char *description,
355 usec_t timeout) {
356
357 _cleanup_free_ char *unit = NULL, *lnk = NULL;
358 _cleanup_free_ char *opt, *p = NULL;
359 _cleanup_fclose_ FILE *f = NULL;
360 int r;
361
362 assert(id);
363 assert(where);
364 assert(description);
365
366 if (options)
605405c6 367 opt = strjoin(options, ",noauto");
7a1494aa
TG
368 else
369 opt = strdup("noauto");
370 if (!opt)
371 return log_oom();
372
373 r = add_mount(id,
374 what,
375 where,
376 fstype,
377 rw,
378 opt,
379 description,
380 NULL);
381 if (r < 0)
382 return r;
383
384 r = unit_name_from_path(where, ".automount", &unit);
385 if (r < 0)
386 return log_error_errno(r, "Failed to generate unit name: %m");
387
605405c6 388 p = strjoin(arg_dest, "/", unit);
7a1494aa
TG
389 if (!p)
390 return log_oom();
391
392 f = fopen(p, "wxe");
393 if (!f)
394 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
395
396 fprintf(f,
397 "# Automatically generated by systemd-gpt-auto-generator\n\n"
398 "[Unit]\n"
399 "Description=%s\n"
400 "Documentation=man:systemd-gpt-auto-generator(8)\n"
401 "[Automount]\n"
402 "Where=%s\n"
70887c5f 403 "TimeoutIdleSec="USEC_FMT"\n",
7a1494aa
TG
404 description,
405 where,
70887c5f 406 timeout / USEC_PER_SEC);
7a1494aa
TG
407
408 r = fflush_and_check(f);
409 if (r < 0)
410 return log_error_errno(r, "Failed to write unit file %s: %m", p);
411
605405c6 412 lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit);
7a1494aa
TG
413 if (!lnk)
414 return log_oom();
415 mkdir_parents_label(lnk, 0755);
416
417 if (symlink(p, lnk) < 0)
418 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
419
420 return 0;
421}
422
72e18a98 423static int add_esp(DissectedPartition *p) {
b52a109a 424 const char *esp;
59512f21
KS
425 int r;
426
72e18a98 427 assert(p);
59512f21 428
59512f21 429 if (in_initrd()) {
b52a109a 430 log_debug("In initrd, ignoring the ESP.");
59512f21
KS
431 return 0;
432 }
433
b52a109a
LP
434 /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
435 esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot";
59512f21 436
0b6b6787 437 /* We create an .automount which is not overridden by the .mount from the fstab generator. */
b9088048
FB
438 r = fstab_is_mount_point(esp);
439 if (r < 0)
440 return log_error_errno(r, "Failed to parse fstab: %m");
441 if (r == 0) {
b52a109a 442 log_debug("%s specified in fstab, ignoring.", esp);
59512f21
KS
443 return 0;
444 }
445
b52a109a
LP
446 if (path_is_busy(esp)) {
447 log_debug("%s already populated, ignoring.", esp);
59512f21
KS
448 return 0;
449 }
450
7ba25ab5 451 if (is_efi_boot()) {
72e18a98 452 sd_id128_t loader_uuid;
59512f21 453
7ba25ab5 454 /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
59512f21 455
7ba25ab5
LP
456 r = efi_loader_get_device_part_uuid(&loader_uuid);
457 if (r == -ENOENT) {
458 log_debug("EFI loader partition unknown.");
459 return 0;
460 }
e28973ee
ZJS
461 if (r < 0)
462 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
7ba25ab5 463
72e18a98 464 if (!sd_id128_equal(p->uuid, loader_uuid)) {
7ba25ab5
LP
465 log_debug("Partition for %s does not appear to be the partition we are booted from.", esp);
466 return 0;
467 }
468 } else
469 log_debug("Not an EFI boot, skipping ESP check.");
470
471 return add_automount("boot",
72e18a98
LP
472 p->node,
473 esp,
474 p->fstype,
475 true,
476 "umask=0077",
477 "EFI System Partition Automount",
478 120 * USEC_PER_SEC);
7a1494aa 479}
59512f21 480#else
d97beb0e 481static int add_esp(DissectedPartition *p) {
59512f21 482 return 0;
59512f21 483}
7a1494aa 484#endif
59512f21 485
72e18a98 486static int open_parent(dev_t devnum, int *ret) {
1ca208fb 487 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
61331eab 488 _cleanup_udev_unref_ struct udev *udev = NULL;
72e18a98
LP
489 const char *name, *devtype, *node;
490 struct udev_device *parent;
61331eab 491 dev_t pn;
72e18a98
LP
492 int fd;
493
494 assert(ret);
1a14a53c 495
61331eab
LP
496 udev = udev_new();
497 if (!udev)
b47d419c 498 return log_oom();
1a14a53c 499
61331eab 500 d = udev_device_new_from_devnum(udev, 'b', devnum);
1ca208fb
ZJS
501 if (!d)
502 return log_oom();
1a14a53c 503
d2a62382
ZJS
504 name = udev_device_get_devnode(d);
505 if (!name)
506 name = udev_device_get_syspath(d);
507 if (!name) {
72e18a98
LP
508 log_debug("Device %u:%u does not have a name, ignoring.", major(devnum), minor(devnum));
509 goto not_found;
d2a62382
ZJS
510 }
511
1a14a53c 512 parent = udev_device_get_parent(d);
fa041593 513 if (!parent) {
d2a62382 514 log_debug("%s: not a partitioned device, ignoring.", name);
72e18a98 515 goto not_found;
fa041593 516 }
1a14a53c 517
61331eab
LP
518 /* Does it have a devtype? */
519 devtype = udev_device_get_devtype(parent);
fa041593 520 if (!devtype) {
d2a62382 521 log_debug("%s: parent doesn't have a device type, ignoring.", name);
72e18a98 522 goto not_found;
fa041593 523 }
61331eab
LP
524
525 /* Is this a disk or a partition? We only care for disks... */
fa041593 526 if (!streq(devtype, "disk")) {
d2a62382 527 log_debug("%s: parent isn't a raw disk, ignoring.", name);
72e18a98 528 goto not_found;
fa041593 529 }
61331eab
LP
530
531 /* Does it have a device node? */
532 node = udev_device_get_devnode(parent);
fa041593 533 if (!node) {
d2a62382 534 log_debug("%s: parent device does not have device node, ignoring.", name);
72e18a98 535 goto not_found;
fa041593 536 }
61331eab 537
d2a62382 538 log_debug("%s: root device %s.", name, node);
61331eab
LP
539
540 pn = udev_device_get_devnum(parent);
72e18a98
LP
541 if (major(pn) == 0) {
542 log_debug("%s: parent device is not a proper block device, ignoring.", name);
543 goto not_found;
61331eab
LP
544 }
545
72e18a98
LP
546 fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
547 if (fd < 0)
548 return log_error_errno(errno, "Failed to open %s: %m", node);
61331eab 549
72e18a98
LP
550 *ret = fd;
551 return 1;
61331eab 552
72e18a98
LP
553not_found:
554 *ret = -1;
555 return 0;
556}
cb971249 557
72e18a98 558static int enumerate_partitions(dev_t devnum) {
61331eab 559
72e18a98
LP
560 _cleanup_close_ int fd = -1;
561 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
562 int r, k;
61331eab 563
72e18a98
LP
564 r = open_parent(devnum, &fd);
565 if (r <= 0)
566 return r;
61331eab 567
72e18a98
LP
568 r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY, &m);
569 if (r == -ENOPKG) {
570 log_debug_errno(r, "No suitable partition table found, ignoring.");
571 return 0;
61331eab 572 }
23bbb0de 573 if (r < 0)
72e18a98 574 return log_error_errno(r, "Failed to dissect: %m");
0238d4c6 575
72e18a98
LP
576 if (m->partitions[PARTITION_SWAP].found) {
577 k = add_swap(m->partitions[PARTITION_SWAP].node);
578 if (k < 0)
579 r = k;
1a14a53c
LP
580 }
581
72e18a98
LP
582 if (m->partitions[PARTITION_ESP].found) {
583 k = add_esp(m->partitions + PARTITION_ESP);
59512f21
KS
584 if (k < 0)
585 r = k;
586 }
587
72e18a98
LP
588 if (m->partitions[PARTITION_HOME].found) {
589 k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition");
73b80ec2
LP
590 if (k < 0)
591 r = k;
592 }
e48fdd84 593
72e18a98
LP
594 if (m->partitions[PARTITION_SRV].found) {
595 k = add_partition_mount(m->partitions + PARTITION_SRV, "srv", "/srv", "Server Data Partition");
73b80ec2
LP
596 if (k < 0)
597 r = k;
598 }
1a14a53c 599
1a14a53c
LP
600 return r;
601}
602
1a14a53c
LP
603static int get_block_device(const char *path, dev_t *dev) {
604 struct stat st;
605 struct statfs sfs;
606
607 assert(path);
608 assert(dev);
609
c6ba0c18
LP
610 /* Get's the block device directly backing a file system. If
611 * the block device is encrypted, returns the device mapper
612 * block device. */
613
e48fdd84 614 if (lstat(path, &st))
1a14a53c
LP
615 return -errno;
616
617 if (major(st.st_dev) != 0) {
618 *dev = st.st_dev;
619 return 1;
620 }
621
e48fdd84 622 if (statfs(path, &sfs) < 0)
1a14a53c
LP
623 return -errno;
624
c51cf056 625 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
f9ac1544 626 return btrfs_get_block_device(path, dev);
1a14a53c
LP
627
628 return 0;
629}
630
c6ba0c18
LP
631static int get_block_device_harder(const char *path, dev_t *dev) {
632 _cleanup_closedir_ DIR *d = NULL;
633 _cleanup_free_ char *p = NULL, *t = NULL;
634 struct dirent *de, *found = NULL;
635 const char *q;
636 unsigned maj, min;
637 dev_t dt;
638 int r;
639
640 assert(path);
641 assert(dev);
642
643 /* Gets the backing block device for a file system, and
644 * handles LUKS encrypted file systems, looking for its
645 * immediate parent, if there is one. */
646
647 r = get_block_device(path, &dt);
648 if (r <= 0)
649 return r;
650
651 if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
652 return -ENOMEM;
653
654 d = opendir(p);
655 if (!d) {
656 if (errno == ENOENT)
657 goto fallback;
658
659 return -errno;
660 }
661
662 FOREACH_DIRENT_ALL(de, d, return -errno) {
663
49bfc877 664 if (dot_or_dot_dot(de->d_name))
c6ba0c18
LP
665 continue;
666
667 if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
668 continue;
669
78f0243a
LP
670 if (found) {
671 _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
672
673 /* We found a device backed by multiple other devices. We don't really support automatic
674 * discovery on such setups, with the exception of dm-verity partitions. In this case there are
6b3d3783 675 * two backing devices: the data partition and the hash partition. We are fine with such
78f0243a
LP
676 * setups, however, only if both partitions are on the same physical device. Hence, let's
677 * verify this. */
678
679 u = strjoin(p, "/", de->d_name, "/../dev");
680 if (!u)
681 return -ENOMEM;
682
683 v = strjoin(p, "/", found->d_name, "/../dev");
684 if (!v)
685 return -ENOMEM;
686
687 r = read_one_line_file(u, &a);
688 if (r < 0) {
689 log_debug_errno(r, "Failed to read %s: %m", u);
690 goto fallback;
691 }
692
693 r = read_one_line_file(v, &b);
694 if (r < 0) {
695 log_debug_errno(r, "Failed to read %s: %m", v);
696 goto fallback;
697 }
698
699 /* Check if the parent device is the same. If not, then the two backing devices are on
700 * different physical devices, and we don't support that. */
701 if (!streq(a, b))
702 goto fallback;
703 }
c6ba0c18
LP
704
705 found = de;
c6ba0c18
LP
706 }
707
708 if (!found)
709 goto fallback;
710
711 q = strjoina(p, "/", found->d_name, "/dev");
712
713 r = read_one_line_file(q, &t);
714 if (r == -ENOENT)
715 goto fallback;
716 if (r < 0)
717 return r;
718
719 if (sscanf(t, "%u:%u", &maj, &min) != 2)
720 return -EINVAL;
721
722 if (maj == 0)
723 goto fallback;
724
725 *dev = makedev(maj, min);
726 return 1;
727
728fallback:
729 *dev = dt;
730 return 1;
731}
732
96287a49 733static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
73b80ec2 734 int r;
1a14a53c 735
73b80ec2 736 assert(key);
1a14a53c 737
1d84ad94 738 if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
1a14a53c 739
1d84ad94 740 r = value ? parse_boolean(value) : 1;
73b80ec2 741 if (r < 0)
d2a62382 742 log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
8086ffac
ZJS
743 else
744 arg_enabled = r;
1a14a53c 745
1d84ad94
LP
746 } else if (streq(key, "root")) {
747
748 if (proc_cmdline_value_missing(key, value))
749 return 0;
73b80ec2
LP
750
751 /* Disable root disk logic if there's a root= value
752 * specified (unless it happens to be "gpt-auto") */
753
754 arg_root_enabled = streq(value, "gpt-auto");
755
2f3dfc6f
LP
756 } else if (streq(key, "roothash")) {
757
758 if (proc_cmdline_value_missing(key, value))
759 return 0;
760
761 /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
762
763 arg_root_enabled = false;
764
73b80ec2
LP
765 } else if (streq(key, "rw") && !value)
766 arg_root_rw = true;
767 else if (streq(key, "ro") && !value)
768 arg_root_rw = false;
73b80ec2
LP
769
770 return 0;
771}
772
01af8c01
LP
773#ifdef ENABLE_EFI
774static int add_root_cryptsetup(void) {
775
776 /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
777 * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
778
779 return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
780}
781#endif
782
73b80ec2
LP
783static int add_root_mount(void) {
784
785#ifdef ENABLE_EFI
786 int r;
787
788 if (!is_efi_boot()) {
789 log_debug("Not a EFI boot, not creating root mount.");
790 return 0;
9a5cb137
ZJS
791 }
792
73b80ec2
LP
793 r = efi_loader_get_device_part_uuid(NULL);
794 if (r == -ENOENT) {
795 log_debug("EFI loader partition unknown, exiting.");
796 return 0;
23bbb0de
MS
797 } else if (r < 0)
798 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
1a14a53c 799
73b80ec2
LP
800 /* OK, we have an ESP partition, this is fantastic, so let's
801 * wait for a root device to show up. A udev rule will create
802 * the link for us under the right name. */
803
7163e1ca
DD
804 if (in_initrd()) {
805 r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
806 if (r < 0)
807 return 0;
01af8c01
LP
808
809 r = add_root_cryptsetup();
810 if (r < 0)
811 return r;
7163e1ca
DD
812 }
813
73b80ec2
LP
814 return add_mount(
815 "root",
98b2f766 816 "/dev/gpt-auto-root",
73b80ec2
LP
817 in_initrd() ? "/sysroot" : "/",
818 NULL,
cca1dfdd 819 arg_root_rw,
59512f21 820 NULL,
73b80ec2
LP
821 "Root Partition",
822 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
823#else
824 return 0;
825#endif
826}
827
828static int add_mounts(void) {
73b80ec2
LP
829 dev_t devno;
830 int r;
831
c6ba0c18 832 r = get_block_device_harder("/", &devno);
23bbb0de
MS
833 if (r < 0)
834 return log_error_errno(r, "Failed to determine block device of root file system: %m");
57ab9f89 835 if (r == 0) {
c6ba0c18 836 r = get_block_device_harder("/usr", &devno);
eafe88e3
TH
837 if (r < 0)
838 return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
57ab9f89 839 if (r == 0) {
eafe88e3
TH
840 log_debug("Neither root nor /usr file system are on a (single) block device.");
841 return 0;
842 }
3db604b9
LP
843 }
844
61331eab 845 return enumerate_partitions(devno);
73b80ec2
LP
846}
847
848int main(int argc, char *argv[]) {
57ab9f89 849 int r = 0, k;
73b80ec2
LP
850
851 if (argc > 1 && argc != 4) {
852 log_error("This program takes three or no arguments.");
853 return EXIT_FAILURE;
854 }
855
856 if (argc > 1)
857 arg_dest = argv[3];
858
859 log_set_target(LOG_TARGET_SAFE);
860 log_parse_environment();
861 log_open();
862
863 umask(0022);
864
75f86906 865 if (detect_container() > 0) {
73b80ec2 866 log_debug("In a container, exiting.");
e48fdd84 867 return EXIT_SUCCESS;
1a14a53c 868 }
3db604b9 869
1d84ad94 870 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
b5884878 871 if (r < 0)
da927ba9 872 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
1a14a53c 873
73b80ec2
LP
874 if (!arg_enabled) {
875 log_debug("Disabled, exiting.");
876 return EXIT_SUCCESS;
877 }
878
879 if (arg_root_enabled)
880 r = add_root_mount();
881
882 if (!in_initrd()) {
73b80ec2
LP
883 k = add_mounts();
884 if (k < 0)
885 r = k;
886 }
887
888 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1a14a53c 889}