1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/statfs.h>
29 #include "alloc-util.h"
30 #include "blkid-util.h"
31 #include "btrfs-util.h"
32 #include "dirent-util.h"
33 #include "dissect-image.h"
37 #include "fstab-util.h"
38 #include "generator.h"
42 #include "mount-util.h"
43 #include "parse-util.h"
44 #include "path-util.h"
45 #include "proc-cmdline.h"
47 #include "stat-util.h"
48 #include "string-util.h"
49 #include "udev-util.h"
50 #include "unit-name.h"
54 static const char *arg_dest
= "/tmp";
55 static bool arg_enabled
= true;
56 static bool arg_root_enabled
= true;
57 static bool arg_root_rw
= false;
59 static int add_cryptsetup(const char *id
, const char *what
, bool rw
, bool require
, char **device
) {
60 _cleanup_free_
char *e
= NULL
, *n
= NULL
, *p
= NULL
, *d
= NULL
;
61 _cleanup_fclose_
FILE *f
= NULL
;
68 r
= unit_name_from_path(what
, ".device", &d
);
70 return log_error_errno(r
, "Failed to generate unit name: %m");
72 e
= unit_name_escape(id
);
76 r
= unit_name_build("systemd-cryptsetup", e
, ".service", &n
);
78 return log_error_errno(r
, "Failed to generate unit name: %m");
80 p
= strjoin(arg_dest
, "/", n
);
86 return log_error_errno(errno
, "Failed to create unit file %s: %m", p
);
89 "# Automatically generated by systemd-gpt-auto-generator\n\n"
91 "Description=Cryptography Setup for %%I\n"
92 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
93 "DefaultDependencies=no\n"
94 "Conflicts=umount.target\n"
95 "BindsTo=dev-mapper-%%i.device %s\n"
96 "Before=umount.target cryptsetup.target\n"
98 "IgnoreOnIsolate=true\n"
101 "RemainAfterExit=yes\n"
102 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
103 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
104 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH
" attach '%s' '%s' '' '%s'\n"
105 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH
" detach '%s'\n",
107 id
, what
, rw
? "" : "read-only",
110 r
= fflush_and_check(f
);
112 return log_error_errno(r
, "Failed to write file %s: %m", p
);
114 r
= generator_add_symlink(arg_dest
, d
, "wants", n
);
121 r
= generator_add_symlink(arg_dest
, "cryptsetup.target", "requires", n
);
125 dmname
= strjoina("dev-mapper-", e
, ".device");
126 r
= generator_add_symlink(arg_dest
, dmname
, "requires", n
);
132 p
= strjoin(arg_dest
, "/dev-mapper-", e
, ".device.d/50-job-timeout-sec-0.conf");
136 mkdir_parents_label(p
, 0755);
137 r
= write_string_file(p
,
138 "# Automatically generated by systemd-gpt-auto-generator\n\n"
141 WRITE_STRING_FILE_CREATE
); /* the binary handles timeouts anyway */
143 return log_error_errno(r
, "Failed to write device drop-in: %m");
145 ret
= strappend("/dev/mapper/", id
);
154 static int add_mount(
161 const char *description
,
164 _cleanup_free_
char *unit
= NULL
, *crypto_what
= NULL
, *p
= NULL
;
165 _cleanup_fclose_
FILE *f
= NULL
;
173 log_debug("Adding %s: %s %s", where
, what
, strna(fstype
));
175 if (streq_ptr(fstype
, "crypto_LUKS")) {
177 r
= add_cryptsetup(id
, what
, rw
, true, &crypto_what
);
185 r
= unit_name_from_path(where
, ".mount", &unit
);
187 return log_error_errno(r
, "Failed to generate unit name: %m");
189 p
= strjoin(arg_dest
, "/", unit
);
195 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
198 "# Automatically generated by systemd-gpt-auto-generator\n\n"
201 "Documentation=man:systemd-gpt-auto-generator(8)\n",
205 fprintf(f
, "Before=%s\n", post
);
207 r
= generator_write_fsck_deps(f
, arg_dest
, what
, where
, fstype
);
219 fprintf(f
, "Type=%s\n", fstype
);
222 fprintf(f
, "Options=%s,%s\n", options
, rw
? "rw" : "ro");
224 fprintf(f
, "Options=%s\n", rw
? "rw" : "ro");
226 r
= fflush_and_check(f
);
228 return log_error_errno(r
, "Failed to write unit file %s: %m", p
);
231 return generator_add_symlink(arg_dest
, post
, "requires", unit
);
235 static bool path_is_busy(const char *where
) {
238 /* already a mountpoint; generators run during reload */
239 r
= path_is_mount_point(where
, NULL
, AT_SYMLINK_FOLLOW
);
243 /* the directory might not exist on a stateless system */
250 /* not a mountpoint but it contains files */
251 if (dir_is_empty(where
) <= 0)
257 static int add_partition_mount(
258 DissectedPartition
*p
,
261 const char *description
) {
265 if (path_is_busy(where
)) {
266 log_debug("%s already populated, ignoring.", where
);
278 SPECIAL_LOCAL_FS_TARGET
);
281 static int add_swap(const char *path
) {
282 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
283 _cleanup_fclose_
FILE *f
= NULL
;
288 /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */
289 r
= fstab_has_fstype("swap");
291 return log_error_errno(r
, "Failed to parse fstab: %m");
293 log_debug("swap specified in fstab, ignoring.");
297 log_debug("Adding swap: %s", path
);
299 r
= unit_name_from_path(path
, ".swap", &name
);
301 return log_error_errno(r
, "Failed to generate unit name: %m");
303 unit
= strjoin(arg_dest
, "/", name
);
307 f
= fopen(unit
, "wxe");
309 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
312 "# Automatically generated by systemd-gpt-auto-generator\n\n"
314 "Description=Swap Partition\n"
315 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
320 r
= fflush_and_check(f
);
322 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
324 return generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
, "wants", name
);
328 static int add_automount(
335 const char *description
,
338 _cleanup_free_
char *unit
= NULL
;
339 _cleanup_free_
char *opt
, *p
= NULL
;
340 _cleanup_fclose_
FILE *f
= NULL
;
348 opt
= strjoin(options
, ",noauto");
350 opt
= strdup("noauto");
365 r
= unit_name_from_path(where
, ".automount", &unit
);
367 return log_error_errno(r
, "Failed to generate unit name: %m");
369 p
= strjoin(arg_dest
, "/", unit
);
375 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
378 "# Automatically generated by systemd-gpt-auto-generator\n\n"
381 "Documentation=man:systemd-gpt-auto-generator(8)\n"
384 "TimeoutIdleSec="USEC_FMT
"\n",
387 timeout
/ USEC_PER_SEC
);
389 r
= fflush_and_check(f
);
391 return log_error_errno(r
, "Failed to write unit file %s: %m", p
);
393 return generator_add_symlink(arg_dest
, SPECIAL_LOCAL_FS_TARGET
, "wants", unit
);
396 static int add_esp(DissectedPartition
*p
) {
403 log_debug("In initrd, ignoring the ESP.");
407 /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
408 esp
= access("/efi/", F_OK
) >= 0 ? "/efi" : "/boot";
410 /* We create an .automount which is not overridden by the .mount from the fstab generator. */
411 r
= fstab_is_mount_point(esp
);
413 return log_error_errno(r
, "Failed to parse fstab: %m");
415 log_debug("%s specified in fstab, ignoring.", esp
);
419 if (path_is_busy(esp
)) {
420 log_debug("%s already populated, ignoring.", esp
);
425 sd_id128_t loader_uuid
;
427 /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
429 r
= efi_loader_get_device_part_uuid(&loader_uuid
);
431 log_debug("EFI loader partition unknown.");
435 return log_error_errno(r
, "Failed to read ESP partition UUID: %m");
437 if (!sd_id128_equal(p
->uuid
, loader_uuid
)) {
438 log_debug("Partition for %s does not appear to be the partition we are booted from.", esp
);
442 log_debug("Not an EFI boot, skipping ESP check.");
444 return add_automount("boot",
450 "EFI System Partition Automount",
454 static int add_esp(DissectedPartition
*p
) {
459 static int open_parent(dev_t devnum
, int *ret
) {
460 _cleanup_udev_device_unref_
struct udev_device
*d
= NULL
;
461 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
462 const char *name
, *devtype
, *node
;
463 struct udev_device
*parent
;
473 d
= udev_device_new_from_devnum(udev
, 'b', devnum
);
477 name
= udev_device_get_devnode(d
);
479 name
= udev_device_get_syspath(d
);
481 log_debug("Device %u:%u does not have a name, ignoring.", major(devnum
), minor(devnum
));
485 parent
= udev_device_get_parent(d
);
487 log_debug("%s: not a partitioned device, ignoring.", name
);
491 /* Does it have a devtype? */
492 devtype
= udev_device_get_devtype(parent
);
494 log_debug("%s: parent doesn't have a device type, ignoring.", name
);
498 /* Is this a disk or a partition? We only care for disks... */
499 if (!streq(devtype
, "disk")) {
500 log_debug("%s: parent isn't a raw disk, ignoring.", name
);
504 /* Does it have a device node? */
505 node
= udev_device_get_devnode(parent
);
507 log_debug("%s: parent device does not have device node, ignoring.", name
);
511 log_debug("%s: root device %s.", name
, node
);
513 pn
= udev_device_get_devnum(parent
);
514 if (major(pn
) == 0) {
515 log_debug("%s: parent device is not a proper block device, ignoring.", name
);
519 fd
= open(node
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
521 return log_error_errno(errno
, "Failed to open %s: %m", node
);
531 static int enumerate_partitions(dev_t devnum
) {
533 _cleanup_close_
int fd
= -1;
534 _cleanup_(dissected_image_unrefp
) DissectedImage
*m
= NULL
;
537 r
= open_parent(devnum
, &fd
);
541 r
= dissect_image(fd
, NULL
, 0, DISSECT_IMAGE_GPT_ONLY
, &m
);
543 log_debug_errno(r
, "No suitable partition table found, ignoring.");
547 return log_error_errno(r
, "Failed to dissect: %m");
549 if (m
->partitions
[PARTITION_SWAP
].found
) {
550 k
= add_swap(m
->partitions
[PARTITION_SWAP
].node
);
555 if (m
->partitions
[PARTITION_ESP
].found
) {
556 k
= add_esp(m
->partitions
+ PARTITION_ESP
);
561 if (m
->partitions
[PARTITION_HOME
].found
) {
562 k
= add_partition_mount(m
->partitions
+ PARTITION_HOME
, "home", "/home", "Home Partition");
567 if (m
->partitions
[PARTITION_SRV
].found
) {
568 k
= add_partition_mount(m
->partitions
+ PARTITION_SRV
, "srv", "/srv", "Server Data Partition");
576 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
581 if (STR_IN_SET(key
, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
583 r
= value
? parse_boolean(value
) : 1;
585 log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value
);
589 } else if (streq(key
, "root")) {
591 if (proc_cmdline_value_missing(key
, value
))
594 /* Disable root disk logic if there's a root= value
595 * specified (unless it happens to be "gpt-auto") */
597 arg_root_enabled
= streq(value
, "gpt-auto");
599 } else if (streq(key
, "roothash")) {
601 if (proc_cmdline_value_missing(key
, value
))
604 /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
606 arg_root_enabled
= false;
608 } else if (streq(key
, "rw") && !value
)
610 else if (streq(key
, "ro") && !value
)
617 static int add_root_cryptsetup(void) {
619 /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
620 * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
622 return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL
);
626 static int add_root_mount(void) {
631 if (!is_efi_boot()) {
632 log_debug("Not a EFI boot, not creating root mount.");
636 r
= efi_loader_get_device_part_uuid(NULL
);
638 log_debug("EFI loader partition unknown, exiting.");
641 return log_error_errno(r
, "Failed to read ESP partition UUID: %m");
643 /* OK, we have an ESP partition, this is fantastic, so let's
644 * wait for a root device to show up. A udev rule will create
645 * the link for us under the right name. */
648 r
= generator_write_initrd_root_device_deps(arg_dest
, "/dev/gpt-auto-root");
652 r
= add_root_cryptsetup();
659 "/dev/gpt-auto-root",
660 in_initrd() ? "/sysroot" : "/",
665 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET
: SPECIAL_LOCAL_FS_TARGET
);
671 static int add_mounts(void) {
675 r
= get_block_device_harder("/", &devno
);
677 return log_error_errno(r
, "Failed to determine block device of root file system: %m");
679 r
= get_block_device_harder("/usr", &devno
);
681 return log_error_errno(r
, "Failed to determine block device of /usr file system: %m");
683 log_debug("Neither root nor /usr file system are on a (single) block device.");
688 return enumerate_partitions(devno
);
691 int main(int argc
, char *argv
[]) {
694 if (argc
> 1 && argc
!= 4) {
695 log_error("This program takes three or no arguments.");
702 log_set_target(LOG_TARGET_SAFE
);
703 log_parse_environment();
708 if (detect_container() > 0) {
709 log_debug("In a container, exiting.");
713 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
715 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
718 log_debug("Disabled, exiting.");
722 if (arg_root_enabled
)
723 r
= add_root_mount();
733 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;