2 This file is part of systemd.
4 Copyright 2013 Lennart Poettering
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.
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.
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/>.
22 #include <sys/statfs.h>
28 #include "alloc-util.h"
29 #include "blkid-util.h"
30 #include "btrfs-util.h"
31 #include "dirent-util.h"
32 #include "dissect-image.h"
36 #include "fstab-util.h"
37 #include "generator.h"
41 #include "mount-util.h"
42 #include "parse-util.h"
43 #include "path-util.h"
44 #include "proc-cmdline.h"
46 #include "stat-util.h"
47 #include "string-util.h"
48 #include "udev-util.h"
49 #include "unit-name.h"
53 static const char *arg_dest
= "/tmp";
54 static bool arg_enabled
= true;
55 static bool arg_root_enabled
= true;
56 static bool arg_root_rw
= false;
58 static int add_cryptsetup(const char *id
, const char *what
, bool rw
, bool require
, char **device
) {
59 _cleanup_free_
char *e
= NULL
, *n
= NULL
, *p
= NULL
, *d
= NULL
, *to
= NULL
;
60 _cleanup_fclose_
FILE *f
= NULL
;
67 r
= unit_name_from_path(what
, ".device", &d
);
69 return log_error_errno(r
, "Failed to generate unit name: %m");
71 e
= unit_name_escape(id
);
75 r
= unit_name_build("systemd-cryptsetup", e
, ".service", &n
);
77 return log_error_errno(r
, "Failed to generate unit name: %m");
79 p
= strjoin(arg_dest
, "/", n
);
85 return log_error_errno(errno
, "Failed to create unit file %s: %m", p
);
88 "# Automatically generated by systemd-gpt-auto-generator\n\n"
90 "Description=Cryptography Setup for %%I\n"
91 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
92 "DefaultDependencies=no\n"
93 "Conflicts=umount.target\n"
94 "BindsTo=dev-mapper-%%i.device %s\n"
95 "Before=umount.target cryptsetup.target\n"
97 "IgnoreOnIsolate=true\n"
100 "RemainAfterExit=yes\n"
101 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
102 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
103 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH
" attach '%s' '%s' '' '%s'\n"
104 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH
" detach '%s'\n",
106 id
, what
, rw
? "" : "read-only",
109 r
= fflush_and_check(f
);
111 return log_error_errno(r
, "Failed to write file %s: %m", p
);
113 r
= generator_add_symlink(arg_dest
, d
, "wants", n
);
120 r
= generator_add_symlink(arg_dest
, "cryptsetup.target", "requires", n
);
124 dmname
= strjoina("dev-mapper-", e
, ".device");
125 r
= generator_add_symlink(arg_dest
, dmname
, "requires", n
);
131 p
= strjoin(arg_dest
, "/dev-mapper-", e
, ".device.d/50-job-timeout-sec-0.conf");
135 mkdir_parents_label(p
, 0755);
136 r
= write_string_file(p
,
137 "# Automatically generated by systemd-gpt-auto-generator\n\n"
140 WRITE_STRING_FILE_CREATE
); /* the binary handles timeouts anyway */
142 return log_error_errno(r
, "Failed to write device drop-in: %m");
144 ret
= strappend("/dev/mapper/", id
);
153 static int add_mount(
160 const char *description
,
163 _cleanup_free_
char *unit
= NULL
, *crypto_what
= NULL
, *p
= NULL
;
164 _cleanup_fclose_
FILE *f
= NULL
;
172 log_debug("Adding %s: %s %s", where
, what
, strna(fstype
));
174 if (streq_ptr(fstype
, "crypto_LUKS")) {
176 r
= add_cryptsetup(id
, what
, rw
, true, &crypto_what
);
184 r
= unit_name_from_path(where
, ".mount", &unit
);
186 return log_error_errno(r
, "Failed to generate unit name: %m");
188 p
= strjoin(arg_dest
, "/", unit
);
194 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
197 "# Automatically generated by systemd-gpt-auto-generator\n\n"
200 "Documentation=man:systemd-gpt-auto-generator(8)\n",
204 fprintf(f
, "Before=%s\n", post
);
206 r
= generator_write_fsck_deps(f
, arg_dest
, what
, where
, fstype
);
218 fprintf(f
, "Type=%s\n", fstype
);
221 fprintf(f
, "Options=%s,%s\n", options
, rw
? "rw" : "ro");
223 fprintf(f
, "Options=%s\n", rw
? "rw" : "ro");
225 r
= fflush_and_check(f
);
227 return log_error_errno(r
, "Failed to write unit file %s: %m", p
);
230 return generator_add_symlink(arg_dest
, post
, "requires", unit
);
234 static bool path_is_busy(const char *where
) {
237 /* already a mountpoint; generators run during reload */
238 r
= path_is_mount_point(where
, NULL
, AT_SYMLINK_FOLLOW
);
242 /* the directory might not exist on a stateless system */
249 /* not a mountpoint but it contains files */
250 if (dir_is_empty(where
) <= 0)
256 static int add_partition_mount(
257 DissectedPartition
*p
,
260 const char *description
) {
264 if (path_is_busy(where
)) {
265 log_debug("%s already populated, ignoring.", where
);
277 SPECIAL_LOCAL_FS_TARGET
);
280 static int add_swap(const char *path
) {
281 _cleanup_free_
char *name
= NULL
, *unit
= NULL
;
282 _cleanup_fclose_
FILE *f
= NULL
;
287 /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */
288 r
= fstab_has_fstype("swap");
290 return log_error_errno(r
, "Failed to parse fstab: %m");
292 log_debug("swap specified in fstab, ignoring.");
296 log_debug("Adding swap: %s", path
);
298 r
= unit_name_from_path(path
, ".swap", &name
);
300 return log_error_errno(r
, "Failed to generate unit name: %m");
302 unit
= strjoin(arg_dest
, "/", name
);
306 f
= fopen(unit
, "wxe");
308 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
311 "# Automatically generated by systemd-gpt-auto-generator\n\n"
313 "Description=Swap Partition\n"
314 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
319 r
= fflush_and_check(f
);
321 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
323 return generator_add_symlink(arg_dest
, SPECIAL_SWAP_TARGET
, "wants", name
);
327 static int add_automount(
334 const char *description
,
337 _cleanup_free_
char *unit
= NULL
;
338 _cleanup_free_
char *opt
, *p
= NULL
;
339 _cleanup_fclose_
FILE *f
= NULL
;
347 opt
= strjoin(options
, ",noauto");
349 opt
= strdup("noauto");
364 r
= unit_name_from_path(where
, ".automount", &unit
);
366 return log_error_errno(r
, "Failed to generate unit name: %m");
368 p
= strjoin(arg_dest
, "/", unit
);
374 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
377 "# Automatically generated by systemd-gpt-auto-generator\n\n"
380 "Documentation=man:systemd-gpt-auto-generator(8)\n"
383 "TimeoutIdleSec="USEC_FMT
"\n",
386 timeout
/ USEC_PER_SEC
);
388 r
= fflush_and_check(f
);
390 return log_error_errno(r
, "Failed to write unit file %s: %m", p
);
392 return generator_add_symlink(arg_dest
, SPECIAL_LOCAL_FS_TARGET
, "wants", unit
);
395 static int add_esp(DissectedPartition
*p
) {
402 log_debug("In initrd, ignoring the ESP.");
406 /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
407 esp
= access("/efi/", F_OK
) >= 0 ? "/efi" : "/boot";
409 /* We create an .automount which is not overridden by the .mount from the fstab generator. */
410 r
= fstab_is_mount_point(esp
);
412 return log_error_errno(r
, "Failed to parse fstab: %m");
414 log_debug("%s specified in fstab, ignoring.", esp
);
418 if (path_is_busy(esp
)) {
419 log_debug("%s already populated, ignoring.", esp
);
424 sd_id128_t loader_uuid
;
426 /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
428 r
= efi_loader_get_device_part_uuid(&loader_uuid
);
430 log_debug("EFI loader partition unknown.");
434 return log_error_errno(r
, "Failed to read ESP partition UUID: %m");
436 if (!sd_id128_equal(p
->uuid
, loader_uuid
)) {
437 log_debug("Partition for %s does not appear to be the partition we are booted from.", esp
);
441 log_debug("Not an EFI boot, skipping ESP check.");
443 return add_automount("boot",
449 "EFI System Partition Automount",
453 static int add_esp(DissectedPartition
*p
) {
458 static int open_parent(dev_t devnum
, int *ret
) {
459 _cleanup_udev_device_unref_
struct udev_device
*d
= NULL
;
460 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
461 const char *name
, *devtype
, *node
;
462 struct udev_device
*parent
;
472 d
= udev_device_new_from_devnum(udev
, 'b', devnum
);
476 name
= udev_device_get_devnode(d
);
478 name
= udev_device_get_syspath(d
);
480 log_debug("Device %u:%u does not have a name, ignoring.", major(devnum
), minor(devnum
));
484 parent
= udev_device_get_parent(d
);
486 log_debug("%s: not a partitioned device, ignoring.", name
);
490 /* Does it have a devtype? */
491 devtype
= udev_device_get_devtype(parent
);
493 log_debug("%s: parent doesn't have a device type, ignoring.", name
);
497 /* Is this a disk or a partition? We only care for disks... */
498 if (!streq(devtype
, "disk")) {
499 log_debug("%s: parent isn't a raw disk, ignoring.", name
);
503 /* Does it have a device node? */
504 node
= udev_device_get_devnode(parent
);
506 log_debug("%s: parent device does not have device node, ignoring.", name
);
510 log_debug("%s: root device %s.", name
, node
);
512 pn
= udev_device_get_devnum(parent
);
513 if (major(pn
) == 0) {
514 log_debug("%s: parent device is not a proper block device, ignoring.", name
);
518 fd
= open(node
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
520 return log_error_errno(errno
, "Failed to open %s: %m", node
);
530 static int enumerate_partitions(dev_t devnum
) {
532 _cleanup_close_
int fd
= -1;
533 _cleanup_(dissected_image_unrefp
) DissectedImage
*m
= NULL
;
536 r
= open_parent(devnum
, &fd
);
540 r
= dissect_image(fd
, NULL
, 0, DISSECT_IMAGE_GPT_ONLY
, &m
);
542 log_debug_errno(r
, "No suitable partition table found, ignoring.");
546 return log_error_errno(r
, "Failed to dissect: %m");
548 if (m
->partitions
[PARTITION_SWAP
].found
) {
549 k
= add_swap(m
->partitions
[PARTITION_SWAP
].node
);
554 if (m
->partitions
[PARTITION_ESP
].found
) {
555 k
= add_esp(m
->partitions
+ PARTITION_ESP
);
560 if (m
->partitions
[PARTITION_HOME
].found
) {
561 k
= add_partition_mount(m
->partitions
+ PARTITION_HOME
, "home", "/home", "Home Partition");
566 if (m
->partitions
[PARTITION_SRV
].found
) {
567 k
= add_partition_mount(m
->partitions
+ PARTITION_SRV
, "srv", "/srv", "Server Data Partition");
575 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
580 if (STR_IN_SET(key
, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
582 r
= value
? parse_boolean(value
) : 1;
584 log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value
);
588 } else if (streq(key
, "root")) {
590 if (proc_cmdline_value_missing(key
, value
))
593 /* Disable root disk logic if there's a root= value
594 * specified (unless it happens to be "gpt-auto") */
596 arg_root_enabled
= streq(value
, "gpt-auto");
598 } else if (streq(key
, "roothash")) {
600 if (proc_cmdline_value_missing(key
, value
))
603 /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
605 arg_root_enabled
= false;
607 } else if (streq(key
, "rw") && !value
)
609 else if (streq(key
, "ro") && !value
)
616 static int add_root_cryptsetup(void) {
618 /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
619 * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
621 return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL
);
625 static int add_root_mount(void) {
630 if (!is_efi_boot()) {
631 log_debug("Not a EFI boot, not creating root mount.");
635 r
= efi_loader_get_device_part_uuid(NULL
);
637 log_debug("EFI loader partition unknown, exiting.");
640 return log_error_errno(r
, "Failed to read ESP partition UUID: %m");
642 /* OK, we have an ESP partition, this is fantastic, so let's
643 * wait for a root device to show up. A udev rule will create
644 * the link for us under the right name. */
647 r
= generator_write_initrd_root_device_deps(arg_dest
, "/dev/gpt-auto-root");
651 r
= add_root_cryptsetup();
658 "/dev/gpt-auto-root",
659 in_initrd() ? "/sysroot" : "/",
664 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET
: SPECIAL_LOCAL_FS_TARGET
);
670 static int add_mounts(void) {
674 r
= get_block_device_harder("/", &devno
);
676 return log_error_errno(r
, "Failed to determine block device of root file system: %m");
678 r
= get_block_device_harder("/usr", &devno
);
680 return log_error_errno(r
, "Failed to determine block device of /usr file system: %m");
682 log_debug("Neither root nor /usr file system are on a (single) block device.");
687 return enumerate_partitions(devno
);
690 int main(int argc
, char *argv
[]) {
693 if (argc
> 1 && argc
!= 4) {
694 log_error("This program takes three or no arguments.");
701 log_set_target(LOG_TARGET_SAFE
);
702 log_parse_environment();
707 if (detect_container() > 0) {
708 log_debug("In a container, exiting.");
712 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, 0);
714 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
717 log_debug("Disabled, exiting.");
721 if (arg_root_enabled
)
722 r
= add_root_mount();
730 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;