]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/gpt-auto-generator/gpt-auto-generator.c
service: add new RootImageOptions feature
[thirdparty/systemd.git] / src / gpt-auto-generator / gpt-auto-generator.c
index de51801d23a69c7302bd2f3462a595bbac1a98a7..02d8837ca9b09b8119a5d2054a8881122257d8e0 100644 (file)
@@ -13,6 +13,7 @@
 #include "device-util.h"
 #include "dirent-util.h"
 #include "dissect-image.h"
+#include "dropin.h"
 #include "efi-loader.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -39,10 +40,73 @@ static bool arg_enabled = true;
 static bool arg_root_enabled = true;
 static int arg_root_rw = -1;
 
+static int open_parent_block_device(dev_t devnum, int *ret_fd) {
+        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+        const char *name, *devtype, *node;
+        sd_device *parent;
+        dev_t pn;
+        int fd, r;
+
+        assert(ret_fd);
+
+        r = sd_device_new_from_devnum(&d, 'b', devnum);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to open device: %m");
+
+        if (sd_device_get_devname(d, &name) < 0) {
+                r = sd_device_get_syspath(d, &name);
+                if (r < 0) {
+                        log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m",
+                                               major(devnum), minor(devnum));
+                        return 0;
+                }
+        }
+
+        r = sd_device_get_parent(d, &parent);
+        if (r < 0) {
+                log_device_debug_errno(d, r, "Not a partitioned device, ignoring: %m");
+                return 0;
+        }
+
+        /* Does it have a devtype? */
+        r = sd_device_get_devtype(parent, &devtype);
+        if (r < 0) {
+                log_device_debug_errno(parent, r, "Parent doesn't have a device type, ignoring: %m");
+                return 0;
+        }
+
+        /* Is this a disk or a partition? We only care for disks... */
+        if (!streq(devtype, "disk")) {
+                log_device_debug(parent, "Parent isn't a raw disk, ignoring.");
+                return 0;
+        }
+
+        /* Does it have a device node? */
+        r = sd_device_get_devname(parent, &node);
+        if (r < 0) {
+                log_device_debug_errno(parent, r, "Parent device does not have device node, ignoring: %m");
+                return 0;
+        }
+
+        log_device_debug(d, "Root device %s.", node);
+
+        r = sd_device_get_devnum(parent, &pn);
+        if (r < 0) {
+                log_device_debug_errno(parent, r, "Parent device is not a proper block device, ignoring: %m");
+                return 0;
+        }
+
+        fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return log_error_errno(errno, "Failed to open %s: %m", node);
+
+        *ret_fd = fd;
+        return 1;
+}
+
 static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
-        _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *id_escaped = NULL, *what_escaped = NULL;
+        _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        const char *p;
         int r;
 
         assert(id);
@@ -60,71 +124,52 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir
         if (r < 0)
                 return log_error_errno(r, "Failed to generate unit name: %m");
 
-        id_escaped = specifier_escape(id);
-        if (!id_escaped)
-                return log_oom();
-
-        what_escaped = specifier_escape(what);
-        if (!what_escaped)
-                return log_oom();
+        r = generator_open_unit_file(arg_dest, NULL, n, &f);
+        if (r < 0)
+                return r;
 
-        p = prefix_roota(arg_dest, n);
-        f = fopen(p, "wxe");
-        if (!f)
-                return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+        r = generator_write_cryptsetup_unit_section(f, NULL);
+        if (r < 0)
+                return r;
 
         fprintf(f,
-                "# Automatically generated by systemd-gpt-auto-generator\n\n"
-                "[Unit]\n"
-                "Description=Cryptography Setup for %%I\n"
-                "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
-                "DefaultDependencies=no\n"
-                "Conflicts=umount.target\n"
-                "BindsTo=dev-mapper-%%i.device %s\n"
                 "Before=umount.target cryptsetup.target\n"
-                "After=%s\n"
-                "IgnoreOnIsolate=true\n"
-                "[Service]\n"
-                "Type=oneshot\n"
-                "RemainAfterExit=yes\n"
-                "TimeoutSec=0\n" /* the binary handles timeouts anyway */
-                "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
-                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
-                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
-                d, d,
-                id_escaped, what_escaped, rw ? "" : "read-only",
-                id_escaped);
+                "Conflicts=umount.target\n"
+                "BindsTo=%s\n"
+                "After=%s\n",
+                d, d);
+
+        r = generator_write_cryptsetup_service_section(f, id, what, NULL, rw ? NULL : "read-only");
+        if (r < 0)
+                return r;
 
         r = fflush_and_check(f);
         if (r < 0)
-                return log_error_errno(r, "Failed to write file %s: %m", p);
+                return log_error_errno(r, "Failed to write file %s: %m", n);
 
         r = generator_add_symlink(arg_dest, d, "wants", n);
         if (r < 0)
                 return r;
 
-        if (require) {
-                const char *dmname;
+        const char *dmname;
+        dmname = strjoina("dev-mapper-", e, ".device");
 
+        if (require) {
                 r = generator_add_symlink(arg_dest, "cryptsetup.target", "requires", n);
                 if (r < 0)
                         return r;
 
-                dmname = strjoina("dev-mapper-", e, ".device");
                 r = generator_add_symlink(arg_dest, dmname, "requires", n);
                 if (r < 0)
                         return r;
         }
 
-        p = strjoina(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf");
-        mkdir_parents_label(p, 0755);
-        r = write_string_file(p,
-                        "# Automatically generated by systemd-gpt-auto-generator\n\n"
-                        "[Unit]\n"
-                        "JobTimeoutSec=0\n",
-                        WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */
+        r = write_drop_in_format(arg_dest, dmname, 50, "job-timeout",
+                                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
+                                 "[Unit]\n"
+                                 "JobTimeoutSec=0"); /* the binary handles timeouts anyway */
         if (r < 0)
-                return log_error_errno(r, "Failed to write device drop-in: %m");
+                log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
 
         if (device) {
                 char *ret;
@@ -162,10 +207,9 @@ static int add_mount(
         assert(where);
         assert(description);
 
-        log_debug("Adding %s: %s %s", where, what, strna(fstype));
+        log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
 
         if (streq_ptr(fstype, "crypto_LUKS")) {
-
                 r = add_cryptsetup(id, what, rw, true, &crypto_what);
                 if (r < 0)
                         return r;
@@ -200,6 +244,10 @@ static int add_mount(
         if (r < 0)
                 return r;
 
+        r = generator_write_blockdev_dependency(f, what);
+        if (r < 0)
+                return r;
+
         fprintf(f,
                 "\n"
                 "[Mount]\n"
@@ -308,7 +356,14 @@ static int add_swap(const char *path) {
                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
                 "[Unit]\n"
                 "Description=Swap Partition\n"
-                "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
+                "Documentation=man:systemd-gpt-auto-generator(8)\n");
+
+        r = generator_write_blockdev_dependency(f, path);
+        if (r < 0)
+                return r;
+
+        fprintf(f,
+                "\n"
                 "[Swap]\n"
                 "What=%s\n",
                 path);
@@ -381,6 +436,19 @@ static int add_automount(
         return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
 }
 
+static const char *esp_or_xbootldr_options(const DissectedPartition *p) {
+        assert(p);
+
+        /* if we probed vfat or have no idea about the file system then assume these file systems are vfat
+         * and thus understand "umask=0077". If we detected something else then don't specify any options and
+         * use kernel defaults. */
+
+        if (!p->fstype || streq(p->fstype, "vfat"))
+                return "umask=0077";
+
+        return NULL;
+}
+
 static int add_xbootldr(DissectedPartition *p) {
         int r;
 
@@ -410,7 +478,7 @@ static int add_xbootldr(DissectedPartition *p) {
                              "/boot",
                              p->fstype,
                              true,
-                             "umask=0077",
+                             esp_or_xbootldr_options(p),
                              "Boot Loader Partition",
                              120 * USEC_PER_SEC);
 }
@@ -484,7 +552,7 @@ static int add_esp(DissectedPartition *p, bool has_xbootldr) {
                              esp_path,
                              p->fstype,
                              true,
-                             "umask=0077",
+                             esp_or_xbootldr_options(p),
                              "EFI System Partition Automount",
                              120 * USEC_PER_SEC);
 }
@@ -530,67 +598,62 @@ static int add_root_rw(DissectedPartition *p) {
         return 0;
 }
 
-static int open_parent_devno(dev_t devnum, int *ret) {
-        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
-        const char *name, *devtype, *node;
-        sd_device *parent;
-        dev_t pn;
-        int fd, r;
-
-        assert(ret);
+#if ENABLE_EFI
+static int add_root_cryptsetup(void) {
 
-        r = sd_device_new_from_devnum(&d, 'b', devnum);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to open device: %m");
+        /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
+         * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
 
-        if (sd_device_get_devname(d, &name) < 0) {
-                r = sd_device_get_syspath(d, &name);
-                if (r < 0) {
-                        log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m", major(devnum), minor(devnum));
-                        return 0;
-                }
-        }
+        return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
+}
+#endif
 
-        r = sd_device_get_parent(d, &parent);
-        if (r < 0) {
-                log_device_debug_errno(d, r, "Not a partitioned device, ignoring: %m");
-                return 0;
-        }
+static int add_root_mount(void) {
+#if ENABLE_EFI
+        int r;
 
-        /* Does it have a devtype? */
-        r = sd_device_get_devtype(parent, &devtype);
-        if (r < 0) {
-                log_device_debug_errno(parent, r, "Parent doesn't have a device type, ignoring: %m");
+        if (!is_efi_boot()) {
+                log_debug("Not a EFI boot, not creating root mount.");
                 return 0;
         }
 
-        /* Is this a disk or a partition? We only care for disks... */
-        if (!streq(devtype, "disk")) {
-                log_device_debug(parent, "Parent isn't a raw disk, ignoring.");
+        r = efi_loader_get_device_part_uuid(NULL);
+        if (r == -ENOENT) {
+                log_notice("EFI loader partition unknown, exiting.\n"
+                           "(The boot loader did not set EFI variable LoaderDevicePartUUID.)");
                 return 0;
-        }
+        } else if (r < 0)
+                return log_error_errno(r, "Failed to read ESP partition UUID: %m");
 
-        /* Does it have a device node? */
-        r = sd_device_get_devname(parent, &node);
-        if (r < 0) {
-                log_device_debug_errno(parent, r, "Parent device does not have device node, ignoring: %m");
-                return 0;
-        }
+        /* OK, we have an ESP partition, this is fantastic, so let's
+         * wait for a root device to show up. A udev rule will create
+         * the link for us under the right name. */
 
-        log_device_debug(d, "Root device %s.", node);
+        if (in_initrd()) {
+                r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
+                if (r < 0)
+                        return 0;
 
-        r = sd_device_get_devnum(parent, &pn);
-        if (r < 0) {
-                log_device_debug_errno(parent, r, "Parent device is not a proper block device, ignoring: %m");
-                return 0;
+                r = add_root_cryptsetup();
+                if (r < 0)
+                        return r;
         }
 
-        fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to open %s: %m", node);
+        /* Note that we do not need to enable systemd-remount-fs.service here. If
+         * /etc/fstab exists, systemd-fstab-generator will pull it in for us. */
 
-        *ret = fd;
-        return 1;
+        return add_mount(
+                        "root",
+                        "/dev/gpt-auto-root",
+                        in_initrd() ? "/sysroot" : "/",
+                        NULL,
+                        arg_root_rw > 0,
+                        NULL,
+                        "Root Partition",
+                        in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
+#else
+        return 0;
+#endif
 }
 
 static int enumerate_partitions(dev_t devnum) {
@@ -598,11 +661,11 @@ static int enumerate_partitions(dev_t devnum) {
         _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
         int r, k;
 
-        r = open_parent_devno(devnum, &fd);
+        r = open_parent_block_device(devnum, &fd);
         if (r <= 0)
                 return r;
 
-        r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
+        r = dissect_image(fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
         if (r == -ENOPKG) {
                 log_debug_errno(r, "No suitable partition table found, ignoring.");
                 return 0;
@@ -640,6 +703,18 @@ static int enumerate_partitions(dev_t devnum) {
                         r = k;
         }
 
+        if (m->partitions[PARTITION_VAR].found) {
+                k = add_partition_mount(m->partitions + PARTITION_VAR, "var", "/var", "Variable Data Partition");
+                if (k < 0)
+                        r = k;
+        }
+
+        if (m->partitions[PARTITION_TMP].found) {
+                k = add_partition_mount(m->partitions + PARTITION_TMP, "var-tmp", "/var/tmp", "Temporary Data Partition");
+                if (k < 0)
+                        r = k;
+        }
+
         if (m->partitions[PARTITION_ROOT].found) {
                 k = add_root_rw(m->partitions + PARTITION_ROOT);
                 if (k < 0)
@@ -649,6 +724,43 @@ static int enumerate_partitions(dev_t devnum) {
         return r;
 }
 
+static int add_mounts(void) {
+        dev_t devno;
+        int r;
+
+        r = get_block_device_harder("/", &devno);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine block device of root file system: %m");
+        if (r == 0) {
+                r = get_block_device_harder("/usr", &devno);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
+                if (r == 0) {
+                        _cleanup_free_ char *p = NULL;
+                        mode_t m;
+
+                        /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
+                         * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
+                         * here. */
+                        r = readlink_malloc("/run/systemd/volatile-root", &p);
+                        if (r == -ENOENT) {
+                                log_debug("Neither root nor /usr file system are on a (single) block device.");
+                                return 0;
+                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
+
+                        r = device_path_parse_major_minor(p, &m, &devno);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse major/minor device node: %m");
+                        if (!S_ISBLK(m))
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
+                }
+        }
+
+        return enumerate_partitions(devno);
+}
+
 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
         int r;
 
@@ -671,7 +783,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 /* Disable root disk logic if there's a root= value
                  * specified (unless it happens to be "gpt-auto") */
 
-                arg_root_enabled = streq(value, "gpt-auto");
+                if (!streq(value, "gpt-auto")) {
+                        arg_root_enabled = false;
+                        log_debug("Disabling root partition auto-detection, root= is defined.");
+                }
 
         } else if (proc_cmdline_key_streq(key, "roothash")) {
 
@@ -690,101 +805,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
         return 0;
 }
 
-#if ENABLE_EFI
-static int add_root_cryptsetup(void) {
-
-        /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
-         * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
-
-        return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
-}
-#endif
-
-static int add_root_mount(void) {
-
-#if ENABLE_EFI
-        int r;
-
-        if (!is_efi_boot()) {
-                log_debug("Not a EFI boot, not creating root mount.");
-                return 0;
-        }
-
-        r = efi_loader_get_device_part_uuid(NULL);
-        if (r == -ENOENT) {
-                log_debug("EFI loader partition unknown, exiting.");
-                return 0;
-        } else if (r < 0)
-                return log_error_errno(r, "Failed to read ESP partition UUID: %m");
-
-        /* OK, we have an ESP partition, this is fantastic, so let's
-         * wait for a root device to show up. A udev rule will create
-         * the link for us under the right name. */
-
-        if (in_initrd()) {
-                r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
-                if (r < 0)
-                        return 0;
-
-                r = add_root_cryptsetup();
-                if (r < 0)
-                        return r;
-        }
-
-        /* Note that we do not need to enable systemd-remount-fs.service here. If
-         * /etc/fstab exists, systemd-fstab-generator will pull it in for us. */
-
-        return add_mount(
-                        "root",
-                        "/dev/gpt-auto-root",
-                        in_initrd() ? "/sysroot" : "/",
-                        NULL,
-                        arg_root_rw > 0,
-                        NULL,
-                        "Root Partition",
-                        in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
-#else
-        return 0;
-#endif
-}
-
-static int add_mounts(void) {
-        dev_t devno;
-        int r;
-
-        r = get_block_device_harder("/", &devno);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine block device of root file system: %m");
-        if (r == 0) {
-                r = get_block_device_harder("/usr", &devno);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
-                if (r == 0) {
-                        _cleanup_free_ char *p = NULL;
-                        mode_t m;
-
-                        /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
-                         * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
-                         * here. */
-                        r = readlink_malloc("/run/systemd/volatile-root", &p);
-                        if (r == -ENOENT) {
-                                log_debug("Neither root nor /usr file system are on a (single) block device.");
-                                return 0;
-                        }
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
-
-                        r = device_path_parse_major_minor(p, &m, &devno);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to parse major/minor device node: %m");
-                        if (!S_ISBLK(m))
-                                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
-                }
-        }
-
-        return enumerate_partitions(devno);
-}
-
 static int run(const char *dest, const char *dest_early, const char *dest_late) {
         int r, k;