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 "blockdev-util.h"
32 #include "btrfs-util.h"
33 #include "dirent-util.h"
34 #include "dissect-image.h"
38 #include "fstab-util.h"
39 #include "generator.h"
43 #include "mount-util.h"
44 #include "parse-util.h"
45 #include "path-util.h"
46 #include "proc-cmdline.h"
48 #include "specifier.h"
49 #include "stat-util.h"
50 #include "string-util.h"
51 #include "udev-util.h"
52 #include "unit-name.h"
56 static const char *arg_dest
= "/tmp";
57 static bool arg_enabled
= true;
58 static bool arg_root_enabled
= true;
59 static bool arg_root_rw
= false;
61 static int add_cryptsetup(const char *id
, const char *what
, bool rw
, bool require
, char **device
) {
62 _cleanup_free_
char *e
= NULL
, *n
= NULL
, *p
= NULL
, *d
= NULL
, *id_escaped
= NULL
, *what_escaped
= NULL
;
63 _cleanup_fclose_
FILE *f
= NULL
;
70 r
= unit_name_from_path(what
, ".device", &d
);
72 return log_error_errno(r
, "Failed to generate unit name: %m");
74 e
= unit_name_escape(id
);
78 r
= unit_name_build("systemd-cryptsetup", e
, ".service", &n
);
80 return log_error_errno(r
, "Failed to generate unit name: %m");
82 id_escaped
= specifier_escape(id
);
86 what_escaped
= specifier_escape(what
);
90 p
= strjoin(arg_dest
, "/", n
);
96 return log_error_errno(errno
, "Failed to create unit file %s: %m", p
);
99 "# Automatically generated by systemd-gpt-auto-generator\n\n"
101 "Description=Cryptography Setup for %%I\n"
102 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
103 "DefaultDependencies=no\n"
104 "Conflicts=umount.target\n"
105 "BindsTo=dev-mapper-%%i.device %s\n"
106 "Before=umount.target cryptsetup.target\n"
108 "IgnoreOnIsolate=true\n"
111 "RemainAfterExit=yes\n"
112 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
113 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
114 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH
" attach '%s' '%s' '' '%s'\n"
115 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH
" detach '%s'\n",
117 id_escaped
, what_escaped
, rw
? "" : "read-only",
120 r
= fflush_and_check(f
);
122 return log_error_errno(r
, "Failed to write file %s: %m", p
);
124 r
= generator_add_symlink(arg_dest
, d
, "wants", n
);
131 r
= generator_add_symlink(arg_dest
, "cryptsetup.target", "requires", n
);
135 dmname
= strjoina("dev-mapper-", e
, ".device");
136 r
= generator_add_symlink(arg_dest
, dmname
, "requires", n
);
142 p
= strjoin(arg_dest
, "/dev-mapper-", e
, ".device.d/50-job-timeout-sec-0.conf");
146 mkdir_parents_label(p
, 0755);
147 r
= write_string_file(p
,
148 "# Automatically generated by systemd-gpt-auto-generator\n\n"
151 WRITE_STRING_FILE_CREATE
); /* the binary handles timeouts anyway */
153 return log_error_errno(r
, "Failed to write device drop-in: %m");
155 ret
= strappend("/dev/mapper/", id
);
164 static int add_mount(
171 const char *description
,
174 _cleanup_free_
char *unit
= NULL
, *crypto_what
= NULL
, *p
= NULL
;
175 _cleanup_fclose_
FILE *f
= NULL
;
178 /* Note that we don't apply specifier escaping on the input strings here, since we know they are not configured
179 * externally, but all originate from our own sources here, and hence we know they contain no % characters that
180 * could potentially be understood as specifiers. */
187 log_debug("Adding %s: %s %s", where
, what
, strna(fstype
));
189 if (streq_ptr(fstype
, "crypto_LUKS")) {
191 r
= add_cryptsetup(id
, what
, rw
, true, &crypto_what
);
199 r
= unit_name_from_path(where
, ".mount", &unit
);
201 return log_error_errno(r
, "Failed to generate unit name: %m");
203 p
= strjoin(arg_dest
, "/", unit
);
209 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
212 "# Automatically generated by systemd-gpt-auto-generator\n\n"
215 "Documentation=man:systemd-gpt-auto-generator(8)\n",
219 fprintf(f
, "Before=%s\n", post
);
221 r
= generator_write_fsck_deps(f
, arg_dest
, what
, where
, fstype
);
233 fprintf(f
, "Type=%s\n", fstype
);
236 fprintf(f
, "Options=%s,%s\n", options
, rw
? "rw" : "ro");
238 fprintf(f
, "Options=%s\n", rw
? "rw" : "ro");
240 r
= fflush_and_check(f
);
242 return log_error_errno(r
, "Failed to write unit file %s: %m", p
);
245 return generator_add_symlink(arg_dest
, post
, "requires", unit
);
249 static bool path_is_busy(const char *where
) {
252 /* already a mountpoint; generators run during reload */
253 r
= path_is_mount_point(where
, NULL
, AT_SYMLINK_FOLLOW
);
257 /* the directory might not exist on a stateless system */
264 /* not a mountpoint but it contains files */
265 if (dir_is_empty(where
) <= 0)
271 static int add_partition_mount(
272 DissectedPartition
*p
,
275 const char *description
) {
279 if (path_is_busy(where
)) {
280 log_debug("%s already populated, ignoring.", where
);
292 SPECIAL_LOCAL_FS_TARGET
);
295 static int add_swap(const char *path
) {
296 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
297 _cleanup_fclose_
FILE *f
= NULL
;
302 /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */
303 r
= fstab_has_fstype("swap");
305 return log_error_errno(r
, "Failed to parse fstab: %m");
307 log_debug("swap specified in fstab, ignoring.");
311 log_debug("Adding swap: %s", path
);
313 r
= unit_name_from_path(path
, ".swap", &name
);
315 return log_error_errno(r
, "Failed to generate unit name: %m");
317 unit
= strjoin(arg_dest
, "/", name
);
321 f
= fopen(unit
, "wxe");
323 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
326 "# Automatically generated by systemd-gpt-auto-generator\n\n"
328 "Description=Swap Partition\n"
329 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
334 r
= fflush_and_check(f
);
336 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
338 return generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
, "wants", name
);
342 static int add_automount(
349 const char *description
,
352 _cleanup_free_
char *unit
= NULL
;
353 _cleanup_free_
char *opt
, *p
= NULL
;
354 _cleanup_fclose_
FILE *f
= NULL
;
362 opt
= strjoin(options
, ",noauto");
364 opt
= strdup("noauto");
379 r
= unit_name_from_path(where
, ".automount", &unit
);
381 return log_error_errno(r
, "Failed to generate unit name: %m");
383 p
= strjoin(arg_dest
, "/", unit
);
389 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
392 "# Automatically generated by systemd-gpt-auto-generator\n\n"
395 "Documentation=man:systemd-gpt-auto-generator(8)\n"
398 "TimeoutIdleSec="USEC_FMT
"\n",
401 timeout
/ USEC_PER_SEC
);
403 r
= fflush_and_check(f
);
405 return log_error_errno(r
, "Failed to write unit file %s: %m", p
);
407 return generator_add_symlink(arg_dest
, SPECIAL_LOCAL_FS_TARGET
, "wants", unit
);
410 static int add_esp(DissectedPartition
*p
) {
417 log_debug("In initrd, ignoring the ESP.");
421 /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
422 esp
= access("/efi/", F_OK
) >= 0 ? "/efi" : "/boot";
424 /* We create an .automount which is not overridden by the .mount from the fstab generator. */
425 r
= fstab_is_mount_point(esp
);
427 return log_error_errno(r
, "Failed to parse fstab: %m");
429 log_debug("%s specified in fstab, ignoring.", esp
);
433 if (path_is_busy(esp
)) {
434 log_debug("%s already populated, ignoring.", esp
);
439 sd_id128_t loader_uuid
;
441 /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
443 r
= efi_loader_get_device_part_uuid(&loader_uuid
);
445 log_debug("EFI loader partition unknown.");
449 return log_error_errno(r
, "Failed to read ESP partition UUID: %m");
451 if (!sd_id128_equal(p
->uuid
, loader_uuid
)) {
452 log_debug("Partition for %s does not appear to be the partition we are booted from.", esp
);
456 log_debug("Not an EFI boot, skipping ESP check.");
458 return add_automount("boot",
464 "EFI System Partition Automount",
468 static int add_esp(DissectedPartition
*p
) {
473 static int open_parent(dev_t devnum
, int *ret
) {
474 _cleanup_udev_device_unref_
struct udev_device
*d
= NULL
;
475 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
476 const char *name
, *devtype
, *node
;
477 struct udev_device
*parent
;
487 d
= udev_device_new_from_devnum(udev
, 'b', devnum
);
491 name
= udev_device_get_devnode(d
);
493 name
= udev_device_get_syspath(d
);
495 log_debug("Device %u:%u does not have a name, ignoring.", major(devnum
), minor(devnum
));
499 parent
= udev_device_get_parent(d
);
501 log_debug("%s: not a partitioned device, ignoring.", name
);
505 /* Does it have a devtype? */
506 devtype
= udev_device_get_devtype(parent
);
508 log_debug("%s: parent doesn't have a device type, ignoring.", name
);
512 /* Is this a disk or a partition? We only care for disks... */
513 if (!streq(devtype
, "disk")) {
514 log_debug("%s: parent isn't a raw disk, ignoring.", name
);
518 /* Does it have a device node? */
519 node
= udev_device_get_devnode(parent
);
521 log_debug("%s: parent device does not have device node, ignoring.", name
);
525 log_debug("%s: root device %s.", name
, node
);
527 pn
= udev_device_get_devnum(parent
);
528 if (major(pn
) == 0) {
529 log_debug("%s: parent device is not a proper block device, ignoring.", name
);
533 fd
= open(node
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
535 return log_error_errno(errno
, "Failed to open %s: %m", node
);
545 static int enumerate_partitions(dev_t devnum
) {
547 _cleanup_close_
int fd
= -1;
548 _cleanup_(dissected_image_unrefp
) DissectedImage
*m
= NULL
;
551 r
= open_parent(devnum
, &fd
);
555 r
= dissect_image(fd
, NULL
, 0, DISSECT_IMAGE_GPT_ONLY
, &m
);
557 log_debug_errno(r
, "No suitable partition table found, ignoring.");
561 return log_error_errno(r
, "Failed to dissect: %m");
563 if (m
->partitions
[PARTITION_SWAP
].found
) {
564 k
= add_swap(m
->partitions
[PARTITION_SWAP
].node
);
569 if (m
->partitions
[PARTITION_ESP
].found
) {
570 k
= add_esp(m
->partitions
+ PARTITION_ESP
);
575 if (m
->partitions
[PARTITION_HOME
].found
) {
576 k
= add_partition_mount(m
->partitions
+ PARTITION_HOME
, "home", "/home", "Home Partition");
581 if (m
->partitions
[PARTITION_SRV
].found
) {
582 k
= add_partition_mount(m
->partitions
+ PARTITION_SRV
, "srv", "/srv", "Server Data Partition");
590 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
595 if (STR_IN_SET(key
, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
597 r
= value
? parse_boolean(value
) : 1;
599 log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value
);
603 } else if (streq(key
, "root")) {
605 if (proc_cmdline_value_missing(key
, value
))
608 /* Disable root disk logic if there's a root= value
609 * specified (unless it happens to be "gpt-auto") */
611 arg_root_enabled
= streq(value
, "gpt-auto");
613 } else if (streq(key
, "roothash")) {
615 if (proc_cmdline_value_missing(key
, value
))
618 /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
620 arg_root_enabled
= false;
622 } else if (streq(key
, "rw") && !value
)
624 else if (streq(key
, "ro") && !value
)
631 static int add_root_cryptsetup(void) {
633 /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
634 * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
636 return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL
);
640 static int add_root_mount(void) {
645 if (!is_efi_boot()) {
646 log_debug("Not a EFI boot, not creating root mount.");
650 r
= efi_loader_get_device_part_uuid(NULL
);
652 log_debug("EFI loader partition unknown, exiting.");
655 return log_error_errno(r
, "Failed to read ESP partition UUID: %m");
657 /* OK, we have an ESP partition, this is fantastic, so let's
658 * wait for a root device to show up. A udev rule will create
659 * the link for us under the right name. */
662 r
= generator_write_initrd_root_device_deps(arg_dest
, "/dev/gpt-auto-root");
666 r
= add_root_cryptsetup();
673 "/dev/gpt-auto-root",
674 in_initrd() ? "/sysroot" : "/",
679 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET
: SPECIAL_LOCAL_FS_TARGET
);
685 static int add_mounts(void) {
689 r
= get_block_device_harder("/", &devno
);
691 return log_error_errno(r
, "Failed to determine block device of root file system: %m");
693 r
= get_block_device_harder("/usr", &devno
);
695 return log_error_errno(r
, "Failed to determine block device of /usr file system: %m");
697 log_debug("Neither root nor /usr file system are on a (single) block device.");
702 return enumerate_partitions(devno
);
705 int main(int argc
, char *argv
[]) {
708 if (argc
> 1 && argc
!= 4) {
709 log_error("This program takes three or no arguments.");
716 log_set_prohibit_ipc(true);
717 log_set_target(LOG_TARGET_AUTO
);
718 log_parse_environment();
723 if (detect_container() > 0) {
724 log_debug("In a container, exiting.");
728 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
730 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
733 log_debug("Disabled, exiting.");
737 if (arg_root_enabled
)
738 r
= add_root_mount();
748 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;