b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/***
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen This file is part of systemd.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright 2013 Lennart Poettering
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is free software; you can redistribute it and/or modify it
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen under the terms of the GNU Lesser General Public License as published by
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen (at your option) any later version.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is distributed in the hope that it will be useful, but
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Lesser General Public License for more details.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen You should have received a copy of the GNU Lesser General Public License
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen***/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <blkid/blkid.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <stdlib.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <sys/statfs.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <unistd.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "libudev.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "sd-id128.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "alloc-util.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "blkid-util.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "btrfs-util.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "dirent-util.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "efivars.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "fd-util.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "fileio.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "fstab-util.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "generator.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "gpt.h"
99634696183dfabae20104e58157c69029a11594Tom Gundersen#include "missing.h"
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen#include "mkdir.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "mount-util.h"
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen#include "parse-util.h"
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen#include "path-util.h"
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen#include "proc-cmdline.h"
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen#include "special.h"
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen#include "stat-util.h"
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen#include "string-util.h"
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen#include "udev-util.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "unit-name.h"
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen#include "util.h"
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen#include "virt.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic const char *arg_dest = "/tmp";
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic bool arg_enabled = true;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic bool arg_root_enabled = true;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic bool arg_root_rw = false;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen _cleanup_fclose_ FILE *f = NULL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen char *from, *ret;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen int r;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert(id);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert(what);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert(device);
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen r = unit_name_from_path(what, ".device", &d);
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (r < 0)
99634696183dfabae20104e58157c69029a11594Tom Gundersen return log_error_errno(r, "Failed to generate unit name: %m");
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen e = unit_name_escape(id);
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (!e)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return log_oom();
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen if (r < 0)
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen return log_error_errno(r, "Failed to generate unit name: %m");
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen p = strjoin(arg_dest, "/", n, NULL);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (!p)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return log_oom();
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen f = fopen(p, "wxe");
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen if (!f)
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen return log_error_errno(errno, "Failed to create unit file %s: %m", p);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen fprintf(f,
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "# Automatically generated by systemd-gpt-auto-generator\n\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "[Unit]\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "Description=Cryptography Setup for %%I\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "DefaultDependencies=no\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "Conflicts=umount.target\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "BindsTo=dev-mapper-%%i.device %s\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "Before=umount.target cryptsetup.target\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "After=%s\n"
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "IgnoreOnIsolate=true\n"
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "[Service]\n"
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "Type=oneshot\n"
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "RemainAfterExit=yes\n"
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "TimeoutSec=0\n" /* the binary handles timeouts anyway */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen d, d,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen id, what, rw ? "" : "read-only",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen id);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = fflush_and_check(f);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r < 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return log_error_errno(r, "Failed to write file %s: %m", p);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen from = strjoina("../", n);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (!to)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_oom();
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen mkdir_parents_label(to, 0755);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (symlink(from, to) < 0)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_error_errno(errno, "Failed to create symlink %s: %m", to);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen free(to);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen if (!to)
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen return log_oom();
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen mkdir_parents_label(to, 0755);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (symlink(from, to) < 0)
99634696183dfabae20104e58157c69029a11594Tom Gundersen return log_error_errno(errno, "Failed to create symlink %s: %m", to);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen free(to);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (!to)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_oom();
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen mkdir_parents_label(to, 0755);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (symlink(from, to) < 0)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_error_errno(errno, "Failed to create symlink %s: %m", to);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen free(p);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (!p)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_oom();
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen mkdir_parents_label(p, 0755);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen r = write_string_file(p,
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "# Automatically generated by systemd-gpt-auto-generator\n\n"
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen "[Unit]\n"
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen "JobTimeoutSec=0\n",
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (r < 0)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_error_errno(r, "Failed to write device drop-in: %m");
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen ret = strappend("/dev/mapper/", id);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (!ret)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_oom();
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen *device = ret;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return 0;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen}
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersenstatic int add_mount(
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen const char *id,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen const char *what,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen const char *where,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen const char *fstype,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen bool rw,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen const char *options,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen const char *description,
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen const char *post) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen _cleanup_fclose_ FILE *f = NULL;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen int r;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(id);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert(what);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert(where);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert(description);
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen log_debug("Adding %s: %s %s", where, what, strna(fstype));
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (streq_ptr(fstype, "crypto_LUKS")) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = add_cryptsetup(id, what, rw, &crypto_what);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r < 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return r;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen what = crypto_what;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen fstype = NULL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen }
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = unit_name_from_path(where, ".mount", &unit);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r < 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return log_error_errno(r, "Failed to generate unit name: %m");
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen p = strjoin(arg_dest, "/", unit, NULL);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (!p)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return log_oom();
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen f = fopen(p, "wxe");
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen if (!f)
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen fprintf(f,
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen "# Automatically generated by systemd-gpt-auto-generator\n\n"
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen "[Unit]\n"
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen "Description=%s\n"
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen "Documentation=man:systemd-gpt-auto-generator(8)\n",
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen description);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (post)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen fprintf(f, "Before=%s\n", post);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r < 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return r;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen fprintf(f,
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen "\n"
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen "[Mount]\n"
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen "What=%s\n"
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen "Where=%s\n",
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen what, where);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (fstype)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen fprintf(f, "Type=%s\n", fstype);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen if (options)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro");
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen else
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = fflush_and_check(f);
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen if (r < 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return log_error_errno(r, "Failed to write unit file %s: %m", p);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (post) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!lnk)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return log_oom();
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen mkdir_parents_label(lnk, 0755);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (symlink(p, lnk) < 0)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen }
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return 0;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic bool path_is_busy(const char *where) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen int r;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen /* already a mountpoint; generators run during reload */
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen if (r > 0)
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen return false;
6f08fb7b34b4eebf664de83c8bc7b9941658aeb4Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen /* the directory might not exist on a stateless system */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r == -ENOENT)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return false;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen 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,
const char *where,
bool rw,
const char *description,
const char *post) {
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype = NULL;
int r;
assert(id);
assert(what);
assert(where);
assert(description);
if (path_is_busy(where)) {
log_debug("%s already populated, ignoring.", where);
return 0;
}
/* Let's check the partition type here, so that we know
* whether to do LUKS magic. */
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_superblocks(b, 1);
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
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);
/* add_mount is OK with fstype being NULL. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
return add_mount(
id,
what,
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) {
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype = NULL, *uuid = NULL;
sd_id128_t id, type_id;
int r;
assert(what);
if (!is_efi_boot()) {
log_debug("Not an EFI boot, ignoring /boot.");
return 0;
}
if (in_initrd()) {
log_debug("In initrd, ignoring /boot.");
return 0;
}
if (detect_container() > 0) {
log_debug("In a container, ignoring /boot.");
return 0;
}
/* We create an .automount which is not overridden by the .mount from the fstab generator. */
if (fstab_is_mount_point("/boot")) {
log_debug("/boot specified in fstab, ignoring.");
return 0;
}
if (path_is_busy("/boot")) {
log_debug("/boot already populated, ignoring.");
return 0;
}
r = efi_loader_get_device_part_uuid(&id);
if (r == -ENOENT) {
log_debug("EFI loader partition unknown.");
return 0;
}
if (r < 0) {
log_error_errno(r, "Failed to read ESP partition UUID: %m");
return r;
}
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 /boot is not a FAT filesystem, ignoring.");
return 0;
}
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL);
if (r != 0) {
log_debug_errno(errno, "Partition for /boot does not have a UUID, ignoring.");
return 0;
}
if (sd_id128_from_string(uuid, &type_id) < 0) {
log_debug("Partition for /boot does not have a valid UUID, ignoring.");
return 0;
}
if (!sd_id128_equal(type_id, id)) {
log_debug("Partition for /boot does not appear to be the partition we are booted from.");
return 0;
}
r = add_automount("boot",
what,
"/boot",
"vfat",
true,
"umask=0077",
"EFI System Partition Automount",
120 * USEC_PER_SEC);
return r;
}
#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 *boot = NULL, *home = NULL, *srv = NULL;
struct udev_list_entry *first, *item;
struct udev_device *parent = NULL;
const char *name, *node, *pttype, *devtype;
int boot_nr = -1, home_nr = -1, srv_nr = -1;
bool home_rw = true, srv_rw = true;
blkid_partlist pl;
int r, k;
dev_t pn;
udev = udev_new();
if (!udev)
return log_oom();
d = udev_device_new_from_devnum(udev, 'b', devnum);
if (!d)
return log_oom();
name = udev_device_get_devnode(d);
if (!name)
name = udev_device_get_syspath(d);
if (!name) {
log_debug("Device %u:%u does not have a name, ignoring.",
major(devnum), minor(devnum));
return 0;
}
parent = udev_device_get_parent(d);
if (!parent) {
log_debug("%s: not a partitioned device, ignoring.", name);
return 0;
}
/* Does it have a devtype? */
devtype = udev_device_get_devtype(parent);
if (!devtype) {
log_debug("%s: parent doesn't have a device type, ignoring.", name);
return 0;
}
/* Is this a disk or a partition? We only care for disks... */
if (!streq(devtype, "disk")) {
log_debug("%s: parent isn't a raw disk, ignoring.", name);
return 0;
}
/* Does it have a device node? */
node = udev_device_get_devnode(parent);
if (!node) {
log_debug("%s: parent device does not have device node, ignoring.", name);
return 0;
}
log_debug("%s: root device %s.", name, node);
pn = udev_device_get_devnum(parent);
if (major(pn) == 0)
return 0;
errno = 0;
b = blkid_new_probe_from_filename(node);
if (!b) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "%s: failed to allocate prober: %m", node);
}
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 == 1)
return 0; /* no results */
else if (r == -2) {
log_warning("%s: probe gave ambiguous results, ignoring.", node);
return 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) {
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")) {
log_debug("%s: not a GPT partition table, ignoring.", node);
return 0;
}
errno = 0;
pl = blkid_probe_get_partitions(b);
if (!pl) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "%s: failed to list partitions: %m", node);
}
e = udev_enumerate_new(udev);
if (!e)
return log_oom();
r = udev_enumerate_add_match_parent(e, parent);
if (r < 0)
return log_oom();
r = udev_enumerate_add_match_subsystem(e, "block");
if (r < 0)
return log_oom();
r = udev_enumerate_scan_devices(e);
if (r < 0)
return log_error_errno(r, "%s: failed to enumerate partitions: %m", node);
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;
q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
if (!q)
continue;
qn = udev_device_get_devnum(q);
if (major(qn) == 0)
continue;
if (qn == devnum)
continue;
if (qn == pn)
continue;
subnode = udev_device_get_devnode(q);
if (!subnode)
continue;
pp = blkid_partlist_devno_to_partition(pl, qn);
if (!pp)
continue;
nr = blkid_partition_get_partno(pp);
if (nr < 0)
continue;
stype = blkid_partition_get_type_string(pp);
if (!stype)
continue;
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;
}
k = add_swap(subnode);
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;
home_nr = nr;
home_rw = !(flags & GPT_FLAG_READ_ONLY),
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;
srv_nr = nr;
srv_rw = !(flags & GPT_FLAG_READ_ONLY),
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)
r = k;
}
if (srv) {
k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
if (k < 0)
r = k;
}
return r;
}
static int get_block_device(const char *path, dev_t *dev) {
struct stat st;
struct statfs sfs;
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;
if (major(st.st_dev) != 0) {
*dev = st.st_dev;
return 1;
}
if (statfs(path, &sfs) < 0)
return -errno;
if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
return btrfs_get_block_device(path, 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;
assert(key);
if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
r = parse_boolean(value);
if (r < 0)
log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
else
arg_enabled = r;
} else if (streq(key, "root") && value) {
/* 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");
} else if (streq(key, "rw") && !value)
arg_root_rw = true;
else if (streq(key, "ro") && !value)
arg_root_rw = false;
return 0;
}
static int add_root_mount(void) {
#ifdef 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. */
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
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");
else 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");
else if (r == 0) {
log_debug("Neither root nor /usr file system are on a (single) block device.");
return 0;
}
}
return enumerate_partitions(devno);
}
int main(int argc, char *argv[]) {
int r = 0;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
arg_dest = argv[3];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
log_open();
umask(0022);
if (detect_container() > 0) {
log_debug("In a container, exiting.");
return EXIT_SUCCESS;
}
r = parse_proc_cmdline(parse_proc_cmdline_item);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
if (!arg_enabled) {
log_debug("Disabled, exiting.");
return EXIT_SUCCESS;
}
if (arg_root_enabled)
r = add_root_mount();
if (!in_initrd()) {
int k;
k = add_mounts();
if (k < 0)
r = k;
}
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}