summaryrefslogtreecommitdiff
path: root/src/gpt-auto-generator/gpt-auto-generator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpt-auto-generator/gpt-auto-generator.c')
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c514
1 files changed, 419 insertions, 95 deletions
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index da5f3b647a..6cc1aad705 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,78 +17,43 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
+#include <blkid/blkid.h>
#include <stdlib.h>
#include <sys/statfs.h>
-#include <blkid/blkid.h>
+#include <unistd.h>
-#include "sd-id128.h"
#include "libudev.h"
-#include "path-util.h"
-#include "util.h"
-#include "mkdir.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "blkid-util.h"
+#include "btrfs-util.h"
+#include "dirent-util.h"
+#include "efivars.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fstab-util.h"
+#include "generator.h"
+#include "gpt.h"
#include "missing.h"
-#include "udev-util.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
#include "special.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "udev-util.h"
#include "unit-name.h"
+#include "util.h"
#include "virt.h"
-#include "generator.h"
-#include "gpt.h"
-#include "fileio.h"
-#include "efivars.h"
-#include "blkid-util.h"
-#include "btrfs-util.h"
static const char *arg_dest = "/tmp";
static bool arg_enabled = true;
static bool arg_root_enabled = true;
static bool arg_root_rw = false;
-static int add_swap(const char *path) {
- _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int r;
-
- assert(path);
-
- log_debug("Adding swap: %s", path);
-
- r = unit_name_from_path(path, ".swap", &name);
- if (r < 0)
- return log_error_errno(r, "Failed to generate unit name: %m");
-
- unit = strjoin(arg_dest, "/", name, NULL);
- if (!unit)
- return log_oom();
-
- f = fopen(unit, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
-
- fprintf(f,
- "# Automatically generated by systemd-gpt-auto-generator\n\n"
- "[Unit]\n"
- "Description=Swap Partition\n"
- "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
- "[Swap]\n"
- "What=%s\n",
- path);
-
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write unit file %s: %m", unit);
-
- lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
- if (!lnk)
- return log_oom();
-
- mkdir_parents_label(lnk, 0755);
- if (symlink(unit, lnk) < 0)
- return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
-
- return 0;
-}
-
static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
_cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
_cleanup_fclose_ FILE *f = NULL;
@@ -142,9 +105,9 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
id, what, rw ? "" : "read-only",
id);
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write file %s: %m", p);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file %s: %m", p);
from = strjoina("../", n);
@@ -202,6 +165,7 @@ static int add_mount(
const char *where,
const char *fstype,
bool rw,
+ const char *options,
const char *description,
const char *post) {
@@ -262,11 +226,14 @@ static int add_mount(
if (fstype)
fprintf(f, "Type=%s\n", fstype);
- fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
+ if (options)
+ fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro");
+ else
+ fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write unit file %s: %m", p);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", p);
if (post) {
lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
@@ -281,6 +248,28 @@ static int add_mount(
return 0;
}
+static bool path_is_busy(const char *where) {
+ int r;
+
+ /* already a mountpoint; generators run during reload */
+ r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
+ if (r > 0)
+ return false;
+
+ /* the directory might not exist on a stateless system */
+ if (r == -ENOENT)
+ return false;
+
+ if (r < 0)
+ return true;
+
+ /* not a mountpoint but it contains files */
+ if (dir_is_empty(where) <= 0)
+ return true;
+
+ return false;
+}
+
static int probe_and_add_mount(
const char *id,
const char *what,
@@ -298,8 +287,7 @@ static int probe_and_add_mount(
assert(where);
assert(description);
- if (path_is_mount_point(where, AT_SYMLINK_FOLLOW) <= 0 &&
- dir_is_empty(where) <= 0) {
+ if (path_is_busy(where)) {
log_debug("%s already populated, ignoring.", where);
return 0;
}
@@ -312,8 +300,7 @@ static int probe_and_add_mount(
if (!b) {
if (errno == 0)
return log_oom();
- log_error_errno(errno, "Failed to allocate prober: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to allocate prober: %m");
}
blkid_probe_enable_superblocks(b, 1);
@@ -335,21 +322,247 @@ static int probe_and_add_mount(
where,
fstype,
rw,
+ NULL,
description,
post);
}
+static int add_swap(const char *path) {
+ _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(path);
+
+ log_debug("Adding swap: %s", path);
+
+ r = unit_name_from_path(path, ".swap", &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ unit = strjoin(arg_dest, "/", name, NULL);
+ if (!unit)
+ return log_oom();
+
+ f = fopen(unit, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+
+ fprintf(f,
+ "# Automatically generated by systemd-gpt-auto-generator\n\n"
+ "[Unit]\n"
+ "Description=Swap Partition\n"
+ "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
+ "[Swap]\n"
+ "What=%s\n",
+ path);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", unit);
+
+ lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
+ if (!lnk)
+ return log_oom();
+
+ mkdir_parents_label(lnk, 0755);
+ if (symlink(unit, lnk) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
+
+ return 0;
+}
+
+#ifdef ENABLE_EFI
+static int add_automount(
+ const char *id,
+ const char *what,
+ const char *where,
+ const char *fstype,
+ bool rw,
+ const char *options,
+ const char *description,
+ usec_t timeout) {
+
+ _cleanup_free_ char *unit = NULL, *lnk = NULL;
+ _cleanup_free_ char *opt, *p = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(id);
+ assert(where);
+ assert(description);
+
+ if (options)
+ opt = strjoin(options, ",noauto", NULL);
+ else
+ opt = strdup("noauto");
+ if (!opt)
+ return log_oom();
+
+ r = add_mount(id,
+ what,
+ where,
+ fstype,
+ rw,
+ opt,
+ description,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = unit_name_from_path(where, ".automount", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ p = strjoin(arg_dest, "/", unit, NULL);
+ if (!p)
+ return log_oom();
+
+ f = fopen(p, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+
+ fprintf(f,
+ "# Automatically generated by systemd-gpt-auto-generator\n\n"
+ "[Unit]\n"
+ "Description=%s\n"
+ "Documentation=man:systemd-gpt-auto-generator(8)\n"
+ "[Automount]\n"
+ "Where=%s\n"
+ "TimeoutIdleSec=%lld\n",
+ description,
+ where,
+ (unsigned long long)timeout / USEC_PER_SEC);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", p);
+
+ lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit, NULL);
+ if (!lnk)
+ return log_oom();
+ mkdir_parents_label(lnk, 0755);
+
+ if (symlink(p, lnk) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
+
+ return 0;
+}
+
+static int add_boot(const char *what) {
+ const char *esp;
+ int r;
+
+ assert(what);
+
+ if (in_initrd()) {
+ log_debug("In initrd, ignoring the ESP.");
+ return 0;
+ }
+
+ if (detect_container() > 0) {
+ log_debug("In a container, ignoring the ESP.");
+ return 0;
+ }
+
+ /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
+ esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot";
+
+ /* We create an .automount which is not overridden by the .mount from the fstab generator. */
+ if (fstab_is_mount_point(esp)) {
+ log_debug("%s specified in fstab, ignoring.", esp);
+ return 0;
+ }
+
+ if (path_is_busy(esp)) {
+ log_debug("%s already populated, ignoring.", esp);
+ return 0;
+ }
+
+ if (is_efi_boot()) {
+ _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+ const char *fstype = NULL, *uuid_string = NULL;
+ sd_id128_t loader_uuid, part_uuid;
+
+ /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
+
+ r = efi_loader_get_device_part_uuid(&loader_uuid);
+ if (r == -ENOENT) {
+ log_debug("EFI loader partition unknown.");
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read ESP partition UUID: %m");
+
+ errno = 0;
+ b = blkid_new_probe_from_filename(what);
+ if (!b) {
+ if (errno == 0)
+ return log_oom();
+ return log_error_errno(errno, "Failed to allocate prober: %m");
+ }
+
+ blkid_probe_enable_partitions(b, 1);
+ blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (r == -2 || r == 1) /* no result or uncertain */
+ return 0;
+ else if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
+
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ if (!streq_ptr(fstype, "vfat")) {
+ log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp);
+ return 0;
+ }
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid_string, NULL);
+ if (r != 0) {
+ log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp);
+ return 0;
+ }
+
+ if (sd_id128_from_string(uuid_string, &part_uuid) < 0) {
+ log_debug("Partition for %s does not have a valid UUID, ignoring.", esp);
+ return 0;
+ }
+
+ if (!sd_id128_equal(part_uuid, loader_uuid)) {
+ log_debug("Partition for %s does not appear to be the partition we are booted from.", esp);
+ return 0;
+ }
+ } else
+ log_debug("Not an EFI boot, skipping ESP check.");
+
+ return add_automount("boot",
+ what,
+ esp,
+ "vfat",
+ true,
+ "umask=0077",
+ "EFI System Partition Automount",
+ 120 * USEC_PER_SEC);
+}
+#else
+static int add_boot(const char *what) {
+ return 0;
+}
+#endif
+
static int enumerate_partitions(dev_t devnum) {
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_free_ char *home = NULL, *srv = NULL;
+ _cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL;
struct udev_list_entry *first, *item;
struct udev_device *parent = NULL;
const char *name, *node, *pttype, *devtype;
- int home_nr = -1, srv_nr = -1;
+ int boot_nr = -1, home_nr = -1, srv_nr = -1;
bool home_rw = true, srv_rw = true;
blkid_partlist pl;
int r, k;
@@ -418,16 +631,22 @@ static int enumerate_partitions(dev_t devnum) {
errno = 0;
r = blkid_do_safeprobe(b);
- if (r == -2 || r == 1) /* no result or uncertain */
+ if (r == 1)
+ return 0; /* no results */
+ else if (r == -2) {
+ log_warning("%s: probe gave ambiguous results, ignoring.", node);
return 0;
- else if (r != 0)
+ } else if (r != 0)
return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
- if (r != 0)
- return log_error_errno(errno ?: EIO,
- "%s: failed to determine partition table type: %m", node);
+ if (r != 0) {
+ if (errno == 0)
+ return 0; /* No partition table found. */
+
+ return log_error_errno(errno, "%s: failed to determine partition table type: %m", node);
+ }
/* We only do this all for GPT... */
if (!streq_ptr(pttype, "gpt")) {
@@ -463,12 +682,12 @@ static int enumerate_partitions(dev_t devnum) {
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
_cleanup_udev_device_unref_ struct udev_device *q;
+ unsigned long long flags;
const char *stype, *subnode;
sd_id128_t type_id;
blkid_partition pp;
dev_t qn;
int nr;
- unsigned long long flags;
q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
if (!q)
@@ -492,13 +711,6 @@ static int enumerate_partitions(dev_t devnum) {
if (!pp)
continue;
- flags = blkid_partition_get_flags(pp);
-
- /* Ignore partitions that are not marked for automatic
- * mounting on discovery */
- if (flags & GPT_FLAG_NO_AUTO)
- continue;
-
nr = blkid_partition_get_partno(pp);
if (nr < 0)
continue;
@@ -510,8 +722,13 @@ static int enumerate_partitions(dev_t devnum) {
if (sd_id128_from_string(stype, &type_id) < 0)
continue;
+ flags = blkid_partition_get_flags(pp);
+
if (sd_id128_equal(type_id, GPT_SWAP)) {
+ if (flags & GPT_FLAG_NO_AUTO)
+ continue;
+
if (flags & GPT_FLAG_READ_ONLY) {
log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode);
continue;
@@ -521,8 +738,27 @@ static int enumerate_partitions(dev_t devnum) {
if (k < 0)
r = k;
+ } else if (sd_id128_equal(type_id, GPT_ESP)) {
+
+ /* We only care for the first /boot partition */
+ if (boot && nr >= boot_nr)
+ continue;
+
+ /* Note that we do not honour the "no-auto"
+ * flag for the ESP, as it is often unset, to
+ * hide it from Windows. */
+
+ boot_nr = nr;
+
+ r = free_and_strdup(&boot, subnode);
+ if (r < 0)
+ return log_oom();
+
} else if (sd_id128_equal(type_id, GPT_HOME)) {
+ if (flags & GPT_FLAG_NO_AUTO)
+ continue;
+
/* We only care for the first /home partition */
if (home && nr >= home_nr)
continue;
@@ -530,13 +766,15 @@ static int enumerate_partitions(dev_t devnum) {
home_nr = nr;
home_rw = !(flags & GPT_FLAG_READ_ONLY),
- free(home);
- home = strdup(subnode);
- if (!home)
+ r = free_and_strdup(&home, subnode);
+ if (r < 0)
return log_oom();
} else if (sd_id128_equal(type_id, GPT_SRV)) {
+ if (flags & GPT_FLAG_NO_AUTO)
+ continue;
+
/* We only care for the first /srv partition */
if (srv && nr >= srv_nr)
continue;
@@ -544,13 +782,18 @@ static int enumerate_partitions(dev_t devnum) {
srv_nr = nr;
srv_rw = !(flags & GPT_FLAG_READ_ONLY),
- free(srv);
- srv = strdup(subnode);
- if (!srv)
+ r = free_and_strdup(&srv, subnode);
+ if (r < 0)
return log_oom();
}
}
+ if (boot) {
+ k = add_boot(boot);
+ if (k < 0)
+ r = k;
+ }
+
if (home) {
k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
if (k < 0)
@@ -573,6 +816,10 @@ static int get_block_device(const char *path, dev_t *dev) {
assert(path);
assert(dev);
+ /* Get's the block device directly backing a file system. If
+ * the block device is encrypted, returns the device mapper
+ * block device. */
+
if (lstat(path, &st))
return -errno;
@@ -590,6 +837,76 @@ static int get_block_device(const char *path, dev_t *dev) {
return 0;
}
+static int get_block_device_harder(const char *path, dev_t *dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *p = NULL, *t = NULL;
+ struct dirent *de, *found = NULL;
+ const char *q;
+ unsigned maj, min;
+ dev_t dt;
+ int r;
+
+ assert(path);
+ assert(dev);
+
+ /* Gets the backing block device for a file system, and
+ * handles LUKS encrypted file systems, looking for its
+ * immediate parent, if there is one. */
+
+ r = get_block_device(path, &dt);
+ if (r <= 0)
+ return r;
+
+ if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
+ return -ENOMEM;
+
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT)
+ goto fallback;
+
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+
+ if (STR_IN_SET(de->d_name, ".", ".."))
+ continue;
+
+ if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+ continue;
+
+ if (found) /* Don't try to support multiple backing block devices */
+ goto fallback;
+
+ found = de;
+ }
+
+ if (!found)
+ goto fallback;
+
+ q = strjoina(p, "/", found->d_name, "/dev");
+
+ r = read_one_line_file(q, &t);
+ if (r == -ENOENT)
+ goto fallback;
+ if (r < 0)
+ return r;
+
+ if (sscanf(t, "%u:%u", &maj, &min) != 2)
+ return -EINVAL;
+
+ if (maj == 0)
+ goto fallback;
+
+ *dev = makedev(maj, min);
+ return 1;
+
+fallback:
+ *dev = dt;
+ return 1;
+}
+
static int parse_proc_cmdline_item(const char *key, const char *value) {
int r;
@@ -639,12 +956,19 @@ static int add_root_mount(void) {
* 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;
+ }
+
return add_mount(
"root",
"/dev/gpt-auto-root",
in_initrd() ? "/sysroot" : "/",
NULL,
arg_root_rw,
+ NULL,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
#else
@@ -656,11 +980,11 @@ static int add_mounts(void) {
dev_t devno;
int r;
- r = get_block_device("/", &devno);
+ r = get_block_device_harder("/", &devno);
if (r < 0)
return log_error_errno(r, "Failed to determine block device of root file system: %m");
else if (r == 0) {
- r = get_block_device("/usr", &devno);
+ 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");
else if (r == 0) {
@@ -689,7 +1013,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- if (detect_container(NULL) > 0) {
+ if (detect_container() > 0) {
log_debug("In a container, exiting.");
return EXIT_SUCCESS;
}